rustiteratorlifetimeborrow-checker

Borrowed value does not live long enough for generic lifetime


The Rust compiler gives me a lifetime error with the following code (which is a minimal working example of some real code):

pub trait TElement: Copy {
    // Details irrelevant
}

pub trait TSet<'a, I, E>
where
    E: 'a + TElement,
    I: IntoIterator<Item = &'a E>
{
    fn elements(&'a self) -> I;
    fn nr_elements(&'a self) -> usize;
}

#[derive(Clone, Copy)]
pub struct Element {
    // Details irrelevant
}

impl TElement for Element {}

pub struct VecSet<E>
where E: TElement
{
    elements: Vec<E>
}

impl<'a, E> TSet<'a, &'a Vec<E>, E> for VecSet<E>
where E: TElement
{
    fn elements(&'a self) -> &'a Vec<E>
    {
        &self.elements
    }

    fn nr_elements(&'a self) -> usize {
        self.elements.len()
    }
}

pub fn split<'a, S, I, E>(s: &'a S) -> Vec<VecSet<E>>
where
    E: 'a + TElement,
    I: IntoIterator<Item = &'a E>,
    S: TSet<'a, I, E>
{
    // Dummy: Produce batches of size 1
    let mut batches = Vec::<VecSet<E>>::with_capacity(s.nr_elements());
    for &element in s.elements() {
        batches.push(VecSet { elements: vec![element] } );
    }
    batches
}

pub trait TCalculator<'a, S, I, E>
where
    E: 'a + TElement,
    I: IntoIterator<Item = &'a E>,
    S: TSet<'a, I, E>
{
    fn calc(&self, set: &'a S) -> i32;
}

pub struct Calculator {}

impl<'a, S, I, E> TCalculator<'a, S, I, E> for Calculator
where
    E: 'a + TElement,
    I: IntoIterator<Item = &'a E>,
    S: TSet<'a, I, E>
{
    fn calc(&self, set: &'a S) -> i32
    {
        set.elements().into_iter().count() as i32
    }
}

pub fn summarize<'a, S, I, E, C>(set: &'a S, calculator: C) -> i32
where
    E: 'a + TElement,
    I: IntoIterator<Item = &'a E>,
    S: TSet<'a, I, E>,
    C: TCalculator<'a, VecSet<E>, &'a Vec<E>, E>
{
    let set_of_sets = split(set);
    set_of_sets
    .iter()
    .map(|s| calculator.calc(s))
    .sum()
}

fn main() {
    let set = VecSet { elements: vec![Element {}, Element {}] };
    let sum: i32 = summarize(&set, Calculator {});
    println!("Sum = {}", sum);  // Supposed to print: Sum = 2
}

Playground

The error is on set_of_sets in the function summarize: Borrowed value does not live long enough. The explanation given: The argument in calculator.calc(s) requires that set_of_sets is borrowed for 'a, but set_of_sets is dropped at the end of the function while still borrowed. I kind of understand why the error comes up, but I do not know how to solve it.

Full compilation error:

error[E0597]: `set_of_sets` does not live long enough
  --> src/main.rs:89:5
   |
81 | pub fn summarize<'a, S, I, E, C>(set: &'a S, calculator: C) -> i32
   |                  -- lifetime `'a` defined here
...
88 |     let set_of_sets = split(set);
   |         ----------- binding `set_of_sets` declared here
89 |     set_of_sets
   |     ^^^^^^^^^^^ borrowed value does not live long enough
90 |     .iter()
91 |     .map(|s| calculator.calc(s))
   |              ------------------ argument requires that `set_of_sets` is borrowed for `'a`
92 |     .sum()
93 | }
   | - `set_of_sets` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.

Solution

  • If you change trait TSet<'a, I, E> to merely trait TSet<E> and make I an impl Trait return type, things simplify drastically and you solve all your lifetime issues.

    pub trait TElement: Copy {
        // Details irrelevant
    }
    
    pub trait TSet<E>
    where
        E: TElement,
    {
        fn elements<'a>(&'a self) -> impl IntoIterator<Item = &'a E>
        where
            E: 'a;
        fn nr_elements(&self) -> usize;
    }
    
    #[derive(Clone, Copy)]
    pub struct Element {
        // Details irrelevant
    }
    
    impl TElement for Element {}
    
    pub struct VecSet<E>
    where
        E: TElement,
    {
        elements: Vec<E>,
    }
    
    impl<E> TSet<E> for VecSet<E>
    where
        E: TElement,
    {
        fn elements<'a>(&'a self) -> impl IntoIterator<Item = &'a E>
        where
            E: 'a,
        {
            &self.elements
        }
    
        fn nr_elements(&self) -> usize {
            self.elements.len()
        }
    }
    
    pub fn split<S, E>(s: &S) -> Vec<VecSet<E>>
    where
        E: TElement,
        S: TSet<E>,
    {
        // Dummy: Produce batches of size 1
        let mut batches = Vec::<VecSet<E>>::with_capacity(s.nr_elements());
        for &element in s.elements() {
            batches.push(VecSet {
                elements: vec![element],
            });
        }
        batches
    }
    
    pub trait TCalculator<S, E>
    where
        E: TElement,
        S: TSet<E>,
    {
        fn calc(&self, set: &S) -> i32;
    }
    
    pub struct Calculator {}
    
    impl<S, E> TCalculator<S, E> for Calculator
    where
        E: TElement,
        S: TSet<E>,
    {
        fn calc(&self, set: &S) -> i32 {
            set.elements().into_iter().count() as i32
        }
    }
    
    pub fn summarize<S, E, C>(set: &S, calculator: C) -> i32
    where
        E: TElement,
        S: TSet<E>,
        C: TCalculator<VecSet<E>, E>,
    {
        let set_of_sets = split(set);
        set_of_sets.iter().map(|s| calculator.calc(s)).sum()
    }
    
    fn main() {
        let set = VecSet {
            elements: vec![Element {}, Element {}],
        };
        let sum: i32 = summarize(&set, Calculator {});
        println!("Sum = {}", sum); // Supposed to print: Sum = 2
    }