How to write a function with lifetime constraints for fields?

Viewed 122

In the example below, I have a lifetime constraint for the one member. The first call to set_one works, however a second call doesn't:

struct One {
    name: String,
}

struct Two<'a> {
    one: Option<&'a One>,
}

fn set_one<'a>(two: &'a mut Two<'a>, one: &'a mut One) {
    one.name = "something".to_owned();
    two.one = Some(one);
}

fn main() {
    let mut one = One { name: "".to_owned() };
    let mut two = Two { one: None };
    
    set_one(&mut two, &mut one);
    set_one(&mut two, &mut one); // error: "two" already borrowed!
}

Playground here.

My understanding is that after the first call to set_one the ownership is given back to the original caller, so when the function returns the borrow for two is gone. But apparently I'm missing something.

What's wrong with the lifetimes of the example above?

1 Answers

For this to make sense you need to recognize that there are 3 lifetimes involved in the calling of set_one

fn set_one<'a, 'b, 'c>(two: &'a mut Two<'b>, one: &'c mut One)

The lifetime of the reference two the lifetime of the reference one and the lifetime of the reference one within Two. These are 'a, 'b and 'c respectively in the signature above.

When you write the signature as

fn set_one<'a>(two: &'a mut Two<'a>, one: &'a mut One)

You are forcing the 3 lifetimes to be the same. And to put it simply this forces these lifetimes to take the 'bigger' lifetime.

Examining the main function:

fn main() {
    let mut one = One { name: "".to_owned() };                       |
    let mut two = Two { one: None };                                 | Lifetime of 'a 
                                                                     |
    set_one(&mut two, &mut one);                                     |
    set_one(&mut two, &mut one);                                     |
}

We can see that implicitly now the struct two is a Two<'a> with the lifetime 'a as marked in the code.

Then when we call set_one we force the lifetime of the three parameters to be 'a.

You could declare the lifetimes as:

fn set_one<'a, 'b>(two: &'a mut Two<'b>, one: &'a mut One)

However, this has a problem which is at the root of what you want to do:

fn set_one<'a>(two: &'a mut Two<'a>, one: &'a mut One) {
    one.name = "something".to_owned();
    two.one = Some(one); // <--- This
}

When you do this two.one = Some(one); you need the reference within Two to live as long as one. Therefore, there is no escaping the fact that you can't call set_one twice because you will need 2 mutable borrows that will live as long as the scope of one in the main function.

Related