Lifetime question - filling temporary Vec of refs

Hello,

Lifetimes continue to confuse me. I really hope someone can help me on this simple code sample.

I've tried various options for inserting lifetimes here, but cannot seem to figure it how to make it compile.

This is a common pattern that I should be able to figure out whereby I have a function ( in this case get_teens) in which I wish to populate a passed in ref to a mutable vector of references (in this example a vector of teens), to values that exist in an owning vector ( in this case people ).

The compiler error is

Compiling playground v0.0.1 (/playground)
error[E0623]: lifetime mismatch
  --> src/main.rs:10:25
   |
7  | fn get_teens(result: &mut Vec<&Person>, people: &Vec<Person>) {
   |                               -------           ------------ these two types are declared with different lifetimes...
...
10 |             result.push(person);
   |                         ^^^^^^ ...but data from `people` flows into `result` here

For more information about this error, try `rustc --explain E0623`.
error: could not compile `playground` due to previous error

#[derive(Debug)]
struct Person {
    name: String,
    age: i32,
}

fn get_teens(result: &mut Vec<&Person>, people: &Vec<Person>) {
    for person in people.iter() {
        if let 13..=19 = person.age {
            result.push(person);
        } 
    }
}

fn main() {
    let people = vec![
        Person {
           name: "Maya".to_string(),
           age: 56,
        },
        Person {
            name: "Chris".to_string(),
            age: 58,
        },
        Person {
            name: "Julia".to_string(),
            age: 15,
        },
        Person {
            name: "Andria".to_string(),
            age: 16,
        }
    ];
    
    {
        let mut teens: Vec<&Person> = Vec::new();
        get_teens(&mut teens, &people);
        for teen in teens {
            println!("teen={:?}", teen);
        }
    }
}

Here you go:

fn get_teens<'res, 'people>(result: &'res mut Vec<&'people Person>, people: &'people Vec<Person>) {
    for person in people.iter() {
        if let 13..=19 = person.age {
            result.push(person);
        } 
    }
}
1 Like

To understand the issue here, you need to understand that all references in a function declaration have lifetimes, even if you don't explicitly annotate them. If you leave them out, the compiler will infer them based on a set of rules listed here.

If we apply those rules to your get_teens function, we get this signature:

fn get_teens<'a, 'b, 'c>(mut result: &'a Vec<&'b Person>, people: &'c Vec<Person>)

Hopefully this makes the 'lifetime mismatch' a bit clearer - people has the lifetime 'c, but the lifetime of the references stored in result is 'b, despite them both pointing at the same data. By adding the explicit annotations as @alice suggested, you make the compiler able to understand that references to the data held by people are flowing into result.

The other issue was that you had mut result: &Vec<& Person> - this makes the result variable mutable (i.e. you can re-assign it), but the type is still an immutable reference, so you wouldn't be able to push to the Vec through it.

What you actually want is result: &mut Vec<&Person>, which is a mutable reference to the Vec.

4 Likes

Alice,

Thank you very much! Your change worked. It also allowed the compiler to reveal that I had not placed the mut in the correct place, which I see you also fixed in your answer.

Chris

Thank you for the additional comments. I modified my original example so that the muts were in the correct place. That was easy for me to see and fix once I applied Alice's fix.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.