What is the concrete lifetime for the lifetime parameter?

I have two questions about lifetime parameters and concrete lifetimes.

Q1:
Consider this uncompilable code: (from a question on StackOverflow)

mod case2 {
    #[derive(Debug)]
    struct Foo {}

    #[derive(Debug)]
    struct Bar2<'b> {
        x: &'b Foo,
    }

    impl<'b> Bar2<'b> {
        fn f(&'b mut self) -> &'b Foo {
            self.x
        }
    }

    fn f4() {
        let foo = Foo {};
        let mut bar2 = Bar2 { x: &foo }; // #1
        bar2.f(); // #2      'bar2' is borrowed as mutable
        let z = bar2.f(); // #3 error: cannot borrow `bar2` as mutable more than once at a time [E0499]
    }
}

After reading the not-so-clear accepted answer, here is my understanding of the problem. Firstly, annotation x: &'a T means 'a is the lifetime of the reference x itself, not the referent (according to this post). In function f4, 'b is bound to the lifetime of the variable bar2 when creating bar2 at line #1, so according to the definition fn f(&'b mut self) -> &'b Foo, the lifetime of reference self created at line #2 is the same as the lifetime of variable bar2, so comes the error at line #3.

However if I change the code of f4 to this:

fn f4() {
    let foo = Foo {};  // #1
    let mut bar2 = Bar2 { x: &foo }; // #2
    println!("{:?}", &bar2); // #3
    bar2.f(); // #4 
}

This compiles fine, which indicates that the lifetime of reference self created at line #4 is not exactly the lifetime of bar2. Instead, it is shortened somehow. Otherwise, line #3 will cause an error.

My question is: what is exactly the concrete lifetime for 'b, and the lifetime of reference self created at line #4? And what are the rules the compiler follows to determine these?

Q2:
This question is about lifetime variance. This code compiles fine:

#[derive(Debug)]
struct Foo<'a> {
    x: &'a i32,
}

impl<'a> Foo<'a> {
    fn set(&mut self, r: &'a i32) {
        self.x = r;
    }
}

fn main() {
    let v = 5; // #1
    let mut w = 7; // #2
    let mut f = Foo { x: &v }; // #3
    println!("f is {:?}", f); // #4
    {
        let r = &mut w; // #5
        *r += 1; // #6
    }
    f.set(&w); // #7
    println!("now f is {:?}", f); // #8
}

I am wondering what is the concrete lifetime for the parameter 'a of f in function main? And what is the concrete lifetime for reference &w at line #7?

The problem here is, the concrete lifetime (say 'a0) for 'a of Foo needs to span from line #3 to line #8 to ensure the reference in Foo is valid. However, the lifetime of reference &w at line #7 doesn't contain line #5 and line #6 (otherwise, it will cause a compilation error). So the lifetime of &w (say 'b) is strictly contained by 'a0. Now we consider variance: 'b is a supertype of 'a0, which means arguments expecting one &'a0 T reference cannot accept a reference with lifetime 'b, so line #7 will cause a compilation error, which doesn't happen.

Generally the compiler only disallows long borrows if they intersect with something that happens later. If the overlap was earlier in the code, it's ok.

fn set(&mut self, r: &'a i32) {
    self.x = r;
}

so this means that r must be valid for the remainder of the 'a lifetime. It doesn't have to have been valid since 'a started.

2 Likes

I guess so , too. Whether there is any documentation about it?

I think this section of The Book is what you’re looking for.

Do you understand why the first version is unsound and the second version is sound?

It can be good to know how the borrow checker works, but I think sometimes a more intuitive understanding can be found in knowing why it's right. Consider this similar code (most changes are in f)

#[derive(Debug)]
struct Foo {
    value: i32,
}

#[derive(Debug)]
struct Bar2<'b> {
    x: &'b Foo,
    y: Box<Foo>,
}

impl<'b> Bar2<'b> {
    fn f(&'b mut self) -> &'b Foo {
        // replace self.y with an updated one
        self.y = Box::new(Foo {
            value: self.y.value + 1,
        });
        println!("self.y = {:?}", self.y);
        // then dereference what's in self.x
        println!("still have a reference to {:?}", self.x);
        self.x = &*self.y;
        self.x
    }
}

fn main() {
    let foo = Foo { value: 10 };
    let mut bar2 = Bar2 {
        x: &foo,
        y: Box::new(Foo { value: 20 }),
    }; // #1
    bar2.f(); // #2      'bar2' is borrowed as mutable
    //let z = bar2.f(); // #3 error: cannot borrow `bar2` as mutable more than once at a time [E0499]
}

Ignoring the lifetime syntax for the moment, this prints

self.y = Foo { value: 21 }
still have a reference to Foo { value: 10 }

However, uncommenting the final line causes it to fail to compile. This is undoubtedly correct, because if it did compile, f would replace self.y, dropping the old Box, and then immediately dereference self.x, which would now be pointing to the contents of a Box that no longer exists. So the compiler is correctly preventing us from shooting ourselves in the foot.

But this isn't the same as the original code. The original code doesn't actually have a self-borrow. So how can the compiler know the difference? Well, that's what lifetimes are for. The signature of f tells the compiler what to expect from it. Writing &'b mut self tells the compiler that (1) f wants permission to modify the contents of self and (2) the borrow that it uses to do that has to last at least as long as 'b. Taken together these things are what allow the return value of f to borrow itself, which is why f can't be called twice.

In my adapted version, if you take the suggestion of the Stack Overflow answer and remove the 'b from &mut self, main now compiles with the last line intact, but f now raises a lifetime error because it's not living up to the function signature that allows main to compile.

2 Likes

In your second question, ownership of v and w is through the end of the main() function scope - from definition to the closing }, unless returned. Thus, any references to those that you make will have a lifetime that is based on the owned duration of the variable referenced. For both cases it is effectively the same duration.

This led me to a variation that I don't quite understand why it doesn't work:

fn main() {
    let v = 5; // #1
    let mut f = Foo { x: &v }; // #2
    println!("f is {:?}", f); // #3
    {
        let w = 7; // #4
        f.set(&w); // #5
        println!("f is {:?}", f); // #6
        f.set(&v); // #7
    } // There is an issue here, even though the reference to w in f should be dropped.
    println!("f is back to {:?}", f); // #8
}

The version above fails: playground
But this works: playground

It seems the lifetime associated with f gets downcast to the shortest lived version (call it &'b w in the new scope), and can't be changed back (so f.x becomes &'b v after f.set(&v) even though &v lives long enough)?

Also, in the first question, the alternate version of f4 that compiles works because it has an immutable reference &bar2 at #3, whereas bar2.f() takes a mutable reference to self.