The “outlives” relation and actual scopes - rust

I was going through the legendary RFC 1214 and it seems that I’m missing something crucial.
struct Foo;
struct Bar<'a> {
foo: &'a Foo
}
fn f<'x, 'y>(_: &'x Foo, _: &'y Bar<'x>)
where 'y: 'x, 'x: 'y {
}
fn g<'x>(x: &'x Foo) {
let y = Bar {foo : x};
f(x, &y); // ?
}
fn main(){
let x = Foo;
g(&x);
}
In this code I went to great lengths to make sure that 'x : 'y and not 'y : 'x. The function that defines x calls the function that defines y, I believe this is already enough to guarantee that x outlives y, but I also put a reference to x inside y, just to make sure.
Now, the constraints in f are such that the invocation of this function can’t possibly be valid. I mean, well, it can, if and only if 'x == 'y, but it totally looks like x lives strictly longer than y, as it is defined in the outer scope.
Nevertheless, this code typechecks and compiles. How is this possible?

Lifetimes have variance, that is, the compiler can choose to shorten the lifetime of a &'a Foo to some &'b Foo. The lifetime of a reference like that just means that the Foo lasts at least as long as 'a: a shorter lifetime still satisfies this guarantee. This is what is happening here: the 'x lifetime is being shortened to have the same lifetime as the &y reference.
You can use invariance to stop this compiling: if the lifetime 'x cannot be shortened, then the code will stop compiling as you expect.
use std::cell::Cell;
struct Foo;
struct Bar<'a> {
foo: Cell<&'a Foo>
}
fn f<'x, 'y>(_: Cell<&'x Foo>, _: &'y Bar<'x>)
where 'y: 'x, 'x: 'y {
}
fn g<'x>(x: Cell<&'x Foo>) {
let y = Bar {foo : x.clone()};
f(x, &y); // ?
}
fn main(){
let x = Foo;
g(Cell::new(&x));
}
<anon>:16:10: 16:11 error: `y` does not live long enough
<anon>:16 f(x, &y); // ?
^
<anon>:14:28: 17:2 note: reference must be valid for the lifetime 'x as defined on the block at 14:27...
<anon>:14 fn g<'x>(x: Cell<&'x Foo>) {
<anon>:15 let y = Bar {foo : x.clone()};
<anon>:16 f(x, &y); // ?
<anon>:17 }
<anon>:15:34: 17:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 15:33
<anon>:15 let y = Bar {foo : x.clone()};
<anon>:16 f(x, &y); // ?
<anon>:17 }
What is happening here is Cell<T> is invariant in T, because it is readable and writable. This in particular means that Cell<&'x Foo> cannot be shortened to Cell<&'y Foo>: filling it with a reference &'y Foo that is truly 'y (i.e. only lasts for 'y) will mean the reference is dangling once the cell leaves 'y (but is still in 'x).

Here are three things which in combination explain the behavior you see:
The 'x on f is a completely different, independent lifetime parameter from the 'x in g. The compiler can choose different concrete lifetimes to substitute for each.
'x : 'y, 'y: 'x means that 'x == 'y (this is not real syntax).
If you have a reference, you can implicitly create another reference with a shorter lifetime from it. Consider for example the function fn mangle_a_string<'a>(_s: &'a str) -> &'a str { "a static string" }
So what happens in f(x, &y) is that the first argument is coerced to a reference with a shorter lifetime, matching the second argument's lifetim, to satisfy the bounds in the where clause.

Related

Rust Lifetime Book confusion

I am very confused about lifetimes, in particular an example in the rust book:
https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
Says that:
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
Needs a lifetime specifier so the compiler knows the lifetime? Doesn't the compiler already know the lifetime? x and y are both parameters to the function, so x and y will last for the whole duration of the function so why would we need a lifetime specifier here? I don't think x and y will ever run into lifetime issues based on the code as is.

Explicit lifetime annotations toy example

I'm trying to figure out a Rust lifetime issue and after boiling it down a bunch, I realized that I have no idea how I would explicitly annotate the lifetimes of r, x2, and _arr in foo:
struct X<'a> {
_v: &'a mut i32,
}
fn main() {
let mut v1 = 0;
let x1 = X { _v: &mut v1 };
foo(x1);
}
fn foo(x1: X) {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
}
I'm going to work off of the assumption that the part of your code that got cut off is the following:
struct X<'a> {
_v: &'a mut i32,
}
In that case, let's dissect what's going on in foo a little bit.
fn foo(x1: X) {
// Lives until the end of the function, call its lifetime 'v2
let mut v2 = 1;
// Borrow v2 for some lifetime 'r which is no longer than 'v2
// so r is of type &'r mut i32
let r = &mut v2;
let x2 = X { _v: r }; // x2 is of type X<'r>
let _arr = [x1, x2]; // ??
}
The confusion probably comes from the fact that x1 seems to have an ambiguous lifetime in its type. This is because we didn't explicitly specify the lifetime in x1: X. This is a Rust 2018 idiom, and I personally recommend not doing this. You can add #![deny(rust_2018_idioms)] to the top of your crate root and the compiler will point out these ambiguities to you and force you to be more explicit. What happens here is that the function declaration gets de-sugared to the following:
fn foo<'x1>(x1: X<'x1>) { ... }
Now it is implied that the lifetime 'x1 extends at least through the body of foo, and this makes sense because it kind of has to. If something which lived for 'x1 was freed in the middle of foo (disregarding how something like that could even happen), then that would defeat the point of using lifetimes for memory safety.
So that being said, let's revisit this line:
let _arr = [x1, x2];
We know that x2 is of the type X<'r> and that 'r extends at most to the end of foo (since 'r is no longer than 'v2 and 'v2 extends to the end of foo). Moreover, we know that x1 is of the type X<'x1> and that 'x1 extends at least to the end of foo. This means that 'x1 is at least as long as 'r. Since lifetimes are covariant, this means that X<'x1> is a sub-type of X<'r>, since whenever we require a value of the type X<'r> we can safely substitute in one of type X<'x1> instead. So what happens is _arr is given the type [X<'r>; 2], and upon construction the lifetime on x1 is shortened to 'r.
We can actually test this hypothesis to see if it's correct. What would happen if the compiler wasn't allowed to shorten that lifetime? In other words, what if the lifetime on X was not covariant. If we modify X as follows then its lifetime type parameter is made invariant:
struct X<'a> {
_v: &'a mut i32,
_invariant: PhantomData<fn(&'a ()) -> &'a ()>
}
And sure enough, after adding the _invariant field in the appropriate places to make the code compile, we receive the following error:
|
14 | fn foo<'x1>(x1: X<'x1>) {
| --- lifetime `'x1` defined here
15 | let mut v2 = 1;
16 | let r = &mut v2;
| ^^^^^^^ borrowed value does not live long enough
17 | let x2 = X { _v: r, _invariant: PhantomData };
| - this usage requires that `v2` is borrowed for `'x1`
18 | let _arr = [x1, x2];
19 | }
| - `v2` dropped here while still borrowed
Now how do we know the compiler wasn't extending the lifetime 'r to 'x1 in the first place? Well if it was doing that, then we could modify foo to do the following, which would unequivocally cause a use-after-free:
fn foo<'x1>(x1: X<'x1>) -> &'x1 mut i32 {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
_arr[1]._v
}
And sure enough if you try the code above it fails to compile with the reason given being that we're returning a reference to a local variable. Moreover, if you try returning _arr[0]._v, then you get the exact same error.
Sub-typing and variance can be pretty hard to grasp and it's not something you need to fully understand to be an effective Rust programmer. Nonetheless, it is very interesting and you can learn more about it here in the Rustonomicon.

Why can't I create a closure that produces mutable references to what it closes on? [duplicate]

I was playing around with Rust closures when I hit this interesting scenario:
fn main() {
let mut y = 10;
let f = || &mut y;
f();
}
This gives an error:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:4:16
|
4 | let f = || &mut y;
| ^^^^^^
|
note: first, the lifetime cannot outlive the lifetime as defined on the body at 4:13...
--> src/main.rs:4:13
|
4 | let f = || &mut y;
| ^^^^^^^^^
note: ...so that closure can access `y`
--> src/main.rs:4:16
|
4 | let f = || &mut y;
| ^^^^^^
note: but, the lifetime must be valid for the call at 6:5...
--> src/main.rs:6:5
|
6 | f();
| ^^^
note: ...so type `&mut i32` of expression is valid during the expression
--> src/main.rs:6:5
|
6 | f();
| ^^^
Even though the compiler is trying to explain it line by line, I still haven't understood what exactly it is complaining about.
Is it trying to say that the mutable reference cannot outlive the enclosing closure?
The compiler does not complain if I remove the call f().
Short version
The closure f stores a mutable reference to y. If it were allowed to return a copy of this reference, you would end up with two simultaneous mutable references to y (one in the closure, one returned), which is forbidden by Rust's memory safety rules.
Long version
The closure can be thought of as
struct __Closure<'a> {
y: &'a mut i32,
}
Since it contains a mutable reference, the closure is called as FnMut, essentially with the definition
fn call_mut(&mut self, args: ()) -> &'a mut i32 { self.y }
Since we only have a mutable reference to the closure itself, we can't move the field y out, neither are we able to copy it, since mutable references aren't Copy.
We can trick the compiler into accepting the code by forcing the closure to be called as FnOnce instead of FnMut. This code works fine:
fn main() {
let x = String::new();
let mut y: u32 = 10;
let f = || {
drop(x);
&mut y
};
f();
}
Since we are consuming x inside the scope of the closure and x is not Copy, the compiler detects that the closure can only be FnOnce. Calling an FnOnce closure passes the closure itself by value, so we are allowed to move the mutable reference out.
Another more explicit way to force the closure to be FnOnce is to pass it to a generic function with a trait bound. This code works fine as well:
fn make_fn_once<'a, T, F: FnOnce() -> T>(f: F) -> F {
f
}
fn main() {
let mut y: u32 = 10;
let f = make_fn_once(|| {
&mut y
});
f();
}
There are two main things at play here:
Closures cannot return references to their environment
A mutable reference to a mutable reference can only use the lifetime of the outer reference (unlike with immutable references)
Closures returning references to environment
Closures cannot return any references with the lifetime of self (the closure object). Why is that? Every closure can be called as FnOnce, since that's the super-trait of FnMut which in turn is the super-trait of Fn. FnOnce has this method:
fn call_once(self, args: Args) -> Self::Output;
Note that self is passed by value. So since self is consumed (and now lives within the call_once function`) we cannot return references to it -- that would be equivalent to returning references to a local function variable.
In theory, the call_mut would allow to return references to self (since it receives &mut self). But since call_once, call_mut and call are all implemented with the same body, closures in general cannot return references to self (that is: to their captured environment).
Just to be sure: closures can capture references and return those! And they can capture by reference and return that reference. Those things are something different. It's just about what is stored in the closure type. If there is a reference stored within the type, it can be returned. But we can't return references to anything stored within the closure type.
Nested mutable references
Consider this function (note that the argument type implies 'inner: 'outer; 'outer being shorter than 'inner):
fn foo<'outer, 'inner>(x: &'outer mut &'inner mut i32) -> &'inner mut i32 {
*x
}
This won't compile. On the first glance, it seems like it should compile, since we're just peeling one layer of references. And it does work for immutable references! But mutable references are different here to preserve soundness.
It's OK to return &'outer mut i32, though. But it's impossible to get a direct reference with the longer (inner) lifetime.
Manually writing the closure
Let's try to hand code the closure you were trying to write:
let mut y = 10;
struct Foo<'a>(&'a mut i32);
impl<'a> Foo<'a> {
fn call<'s>(&'s mut self) -> &'??? mut i32 { self.0 }
}
let mut f = Foo(&mut y);
f.call();
What lifetime should the returned reference have?
It can't be 'a, because we basically have a &'s mut &'a mut i32. And as discussed above, in such a nested mutable reference situation, we can't extract the longer lifetime!
But it also can't be 's since that would mean the closure returns something with the lifetime of 'self ("borrowed from self"). And as discussed above, closures can't do that.
So the compiler can't generate the closure impls for us.
Consider this code:
fn main() {
let mut y: u32 = 10;
let ry = &mut y;
let f = || ry;
f();
}
It works because the compiler is able to infer ry's lifetime: the reference ry lives in the same scope of y.
Now, the equivalent version of your code:
fn main() {
let mut y: u32 = 10;
let f = || {
let ry = &mut y;
ry
};
f();
}
Now the compiler assigns to ry a lifetime associated to the scope of the closure body, not to the lifetime associated with the main body.
Also note that the immutable reference case works:
fn main() {
let mut y: u32 = 10;
let f = || {
let ry = &y;
ry
};
f();
}
This is because &T has copy semantics and &mut T has move semantics, see Copy/move semantics documentation of &T/&mut T types itself for more details.
The missing piece
The compiler throws an error related to a lifetime:
cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
but as pointed out by Sven Marnach there is also a problem related to the error
cannot move out of borrowed content
But why doesn't the compiler throw this error?
The short answer is that the compiler first executes type checking and then borrow checking.
the long answer
A closure is made up of two pieces:
the state of the closure: a struct containing all the variables captured by the closure
the logic of the closure: an implementation of the FnOnce, FnMut or Fn trait
In this case the state of the closure is the mutable reference y and the logic is the body of the closure { &mut y } that simply returns a mutable reference.
When a reference is encountered, Rust controls two aspects:
the state: if the reference points to a valid memory slice, (i.e. the read-only part of lifetime validity);
the logic: if the memory slice is aliased, in other words if it is pointed from more than one reference simultaneously;
Note the move out from borrowed content is forbidden for avoiding memory aliasing.
The Rust compiler executes its job through several stages, here's a simplified workflow:
.rs input -> AST -> HIR -> HIR postprocessing -> MIR -> HIR postprocessing -> LLVM IR -> binary
The compiler reports a lifetime problem because it first executes the type checking phase in HIR postprocessing (which comprises lifetime analysis) and after that, if successful, executes borrow checking in the MIR postprocessing phase.

Why can I not return a mutable reference to an outer variable from a closure?

I was playing around with Rust closures when I hit this interesting scenario:
fn main() {
let mut y = 10;
let f = || &mut y;
f();
}
This gives an error:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:4:16
|
4 | let f = || &mut y;
| ^^^^^^
|
note: first, the lifetime cannot outlive the lifetime as defined on the body at 4:13...
--> src/main.rs:4:13
|
4 | let f = || &mut y;
| ^^^^^^^^^
note: ...so that closure can access `y`
--> src/main.rs:4:16
|
4 | let f = || &mut y;
| ^^^^^^
note: but, the lifetime must be valid for the call at 6:5...
--> src/main.rs:6:5
|
6 | f();
| ^^^
note: ...so type `&mut i32` of expression is valid during the expression
--> src/main.rs:6:5
|
6 | f();
| ^^^
Even though the compiler is trying to explain it line by line, I still haven't understood what exactly it is complaining about.
Is it trying to say that the mutable reference cannot outlive the enclosing closure?
The compiler does not complain if I remove the call f().
Short version
The closure f stores a mutable reference to y. If it were allowed to return a copy of this reference, you would end up with two simultaneous mutable references to y (one in the closure, one returned), which is forbidden by Rust's memory safety rules.
Long version
The closure can be thought of as
struct __Closure<'a> {
y: &'a mut i32,
}
Since it contains a mutable reference, the closure is called as FnMut, essentially with the definition
fn call_mut(&mut self, args: ()) -> &'a mut i32 { self.y }
Since we only have a mutable reference to the closure itself, we can't move the field y out, neither are we able to copy it, since mutable references aren't Copy.
We can trick the compiler into accepting the code by forcing the closure to be called as FnOnce instead of FnMut. This code works fine:
fn main() {
let x = String::new();
let mut y: u32 = 10;
let f = || {
drop(x);
&mut y
};
f();
}
Since we are consuming x inside the scope of the closure and x is not Copy, the compiler detects that the closure can only be FnOnce. Calling an FnOnce closure passes the closure itself by value, so we are allowed to move the mutable reference out.
Another more explicit way to force the closure to be FnOnce is to pass it to a generic function with a trait bound. This code works fine as well:
fn make_fn_once<'a, T, F: FnOnce() -> T>(f: F) -> F {
f
}
fn main() {
let mut y: u32 = 10;
let f = make_fn_once(|| {
&mut y
});
f();
}
There are two main things at play here:
Closures cannot return references to their environment
A mutable reference to a mutable reference can only use the lifetime of the outer reference (unlike with immutable references)
Closures returning references to environment
Closures cannot return any references with the lifetime of self (the closure object). Why is that? Every closure can be called as FnOnce, since that's the super-trait of FnMut which in turn is the super-trait of Fn. FnOnce has this method:
fn call_once(self, args: Args) -> Self::Output;
Note that self is passed by value. So since self is consumed (and now lives within the call_once function`) we cannot return references to it -- that would be equivalent to returning references to a local function variable.
In theory, the call_mut would allow to return references to self (since it receives &mut self). But since call_once, call_mut and call are all implemented with the same body, closures in general cannot return references to self (that is: to their captured environment).
Just to be sure: closures can capture references and return those! And they can capture by reference and return that reference. Those things are something different. It's just about what is stored in the closure type. If there is a reference stored within the type, it can be returned. But we can't return references to anything stored within the closure type.
Nested mutable references
Consider this function (note that the argument type implies 'inner: 'outer; 'outer being shorter than 'inner):
fn foo<'outer, 'inner>(x: &'outer mut &'inner mut i32) -> &'inner mut i32 {
*x
}
This won't compile. On the first glance, it seems like it should compile, since we're just peeling one layer of references. And it does work for immutable references! But mutable references are different here to preserve soundness.
It's OK to return &'outer mut i32, though. But it's impossible to get a direct reference with the longer (inner) lifetime.
Manually writing the closure
Let's try to hand code the closure you were trying to write:
let mut y = 10;
struct Foo<'a>(&'a mut i32);
impl<'a> Foo<'a> {
fn call<'s>(&'s mut self) -> &'??? mut i32 { self.0 }
}
let mut f = Foo(&mut y);
f.call();
What lifetime should the returned reference have?
It can't be 'a, because we basically have a &'s mut &'a mut i32. And as discussed above, in such a nested mutable reference situation, we can't extract the longer lifetime!
But it also can't be 's since that would mean the closure returns something with the lifetime of 'self ("borrowed from self"). And as discussed above, closures can't do that.
So the compiler can't generate the closure impls for us.
Consider this code:
fn main() {
let mut y: u32 = 10;
let ry = &mut y;
let f = || ry;
f();
}
It works because the compiler is able to infer ry's lifetime: the reference ry lives in the same scope of y.
Now, the equivalent version of your code:
fn main() {
let mut y: u32 = 10;
let f = || {
let ry = &mut y;
ry
};
f();
}
Now the compiler assigns to ry a lifetime associated to the scope of the closure body, not to the lifetime associated with the main body.
Also note that the immutable reference case works:
fn main() {
let mut y: u32 = 10;
let f = || {
let ry = &y;
ry
};
f();
}
This is because &T has copy semantics and &mut T has move semantics, see Copy/move semantics documentation of &T/&mut T types itself for more details.
The missing piece
The compiler throws an error related to a lifetime:
cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
but as pointed out by Sven Marnach there is also a problem related to the error
cannot move out of borrowed content
But why doesn't the compiler throw this error?
The short answer is that the compiler first executes type checking and then borrow checking.
the long answer
A closure is made up of two pieces:
the state of the closure: a struct containing all the variables captured by the closure
the logic of the closure: an implementation of the FnOnce, FnMut or Fn trait
In this case the state of the closure is the mutable reference y and the logic is the body of the closure { &mut y } that simply returns a mutable reference.
When a reference is encountered, Rust controls two aspects:
the state: if the reference points to a valid memory slice, (i.e. the read-only part of lifetime validity);
the logic: if the memory slice is aliased, in other words if it is pointed from more than one reference simultaneously;
Note the move out from borrowed content is forbidden for avoiding memory aliasing.
The Rust compiler executes its job through several stages, here's a simplified workflow:
.rs input -> AST -> HIR -> HIR postprocessing -> MIR -> HIR postprocessing -> LLVM IR -> binary
The compiler reports a lifetime problem because it first executes the type checking phase in HIR postprocessing (which comprises lifetime analysis) and after that, if successful, executes borrow checking in the MIR postprocessing phase.

Who borrowed a variable?

I'm fighting with the borrow checker. I have two similar pieces of code, one working as I expect, and the other not.
The one that works as I expect:
mod case1 {
struct Foo {}
struct Bar1 {
x: Foo,
}
impl Bar1 {
fn f<'a>(&'a mut self) -> &'a Foo {
&self.x
}
}
// only for example
fn f1() {
let mut bar = Bar1 { x: Foo {} };
let y = bar.f(); // (1) 'bar' is borrowed by 'y'
let z = bar.f(); // error (as expected) : cannot borrow `bar` as mutable more
// than once at a time [E0499]
}
fn f2() {
let mut bar = Bar1 { x: Foo {} };
bar.f(); // (2) 'bar' is not borrowed after the call
let z = bar.f(); // ok (as expected)
}
}
The one that doesn't:
mod case2 {
struct Foo {}
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 };
bar2.f(); // (3) 'bar2' is borrowed as mutable, but who borrowed it?
let z = bar2.f(); // error: cannot borrow `bar2` as mutable more than once at a time [E0499]
}
}
I hoped I could call Bar2::f twice without irritating the compiler, as in case 1.
The question is in the comment (3): who borrowed bar2, whereas there is no affectation?
Here's what I understand:
In case 1, f2 call: the lifetime parameter 'a is the one of the receiving &Foo value, so this lifetime is empty when there is no affectation, and bar is not borrowed after the Bar1::f call;
In case 2, bar2 borrows foo (as immutable), so the lifetime parameter 'b in Bar2 struct is the foo reference lifetime, which ends at the end of f4 body. Calling Bar2::f borrows bar2 for that lifetime, namely to the end of f4.
But the question is still: who borrowed bar2? Could it be Bar2::f? How Bar2::f would hold the borrowed ownership after the call? What am I missing here?
I'm using Rust 1.14.0-nightly (86affcdf6 2016-09-28) on x86_64-pc-windows-msvc.
Ah... you basically self-borrowed yourself.
The issue hinges on the fact that you have the same lifetime ('b) used for both the lifetime of Foo and the lifetime of Bar. The compiler then dutifully unifies those lifetimes, and you end up in a strange situation where suddenly the lifetime of the borrow which should have ended at the end of the statement instead ends after the value should have gone out of scope.
As a rule of thumb: always use a fresh lifetime for self. Anything else is weird.
It's interesting to note that this pattern can actually be useful (though more likely with an immutable borrow): it allows anchoring a value to a stack frame, preventing any move after the call to the function, which is (sometimes) useful to represent a borrow that is not well-modeled by Rust (like passing a pointer to the value to FFI).
In case #2, you have this:
impl<'b> Bar2<'b> {
fn f(&'b mut self) -> &'b Foo {
self.x
}
}
To highlight: &'b mut self and &'b Foo have the same lifetime specified.
This is saying that the reference to self and the returned reference to an instance of a Foo both have the same lifetime. Looking at the call site, you have this:
let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
So the compiler is inferring that both foo and bar2 have the same lifetime. The lifetime of foo is the scope of the f4 function, and so the mutable reference to bar2 shares this.
One way to fix this, is to remove the explicit lifetime on the self reference:
fn f(&mut self) -> &'b Foo
This compiles and the compiler correctly understands that the reference to bar2 and the reference to foo have different lifetimes.
Playground: https://play.rust-lang.org/?gist=caf262dd628cf14cc2884a3af842276a&version=stable&backtrace=0
TLDR: Yes, having the same lifetime specifier on the self reference and the returned reference means that the entire scope of f4 holds a mutable borrow of bar2.
I put the body of f4() in a main() and implemented Drop for Bar2 to find out when it is dropped (i.e. goes out of scope):
impl<'b> Drop for Bar2<'b> {
fn drop(&mut self) { println!("dropping Bar2!"); }
}
And the result was:
error: `bar2` does not live long enough
--> <anon>:24:5
|
24 | bar2.f();
| ^^^^ does not live long enough
25 | }
| - borrowed value dropped before borrower
|
= note: values in a scope are dropped in the opposite order they are created
Something's fishy; let's examine it in detail, with helper scopes:
fn main() {
{
let foo = Foo {}; // foo scope begins
{
let mut bar2 = Bar2 { x: &foo }; // bar2 scope begins; bar2 borrows foo
bar2.f();
} // bar2 should be dropped here, but it has the same lifetime as foo, which is still live
} // foo is dropped (its scope ends)
}
It looks to me that there is a leak here and bar2 is never dropped (and thus Drop cannot be implemented for it). That's why you cannot re-borrow it.
I would like to add about the roles that subtyping/variance play here.
&mut T is invariant over T. Given two types T and U, where T < U (T is a subtype of U), then &mut T has no subtyping relation with &mut U (i.e they are invariant with each other), whereas &T is a subtype of &U (&T < &U). But &'lifetime and &'lifetime mut, both are covariant over 'lifetime. So given two lifetimes 'a and 'b for a type T, where 'a outlives 'b, then as per subtyping relation &'a T < &'b T, similarly &'a mut T < &'b mut T
Coming to the question, in the call to function f, self is a reference to Bar2<'a>. The compiler will see if it can "temporarily shorten" the life of bar2 to fit around the scope of the function f's invocation say 'x, as if bar2 and foo were created just before f is called and go away immediately after f (i.e temporary shortening: assuming variable bar2 created within 'x and hence Bar2<'a> to Bar2<'x>, 'a being the original (real) lifetime). But here, "shortening" is not possible; One, because of mutable reference to self and two, same lifetime on references to Foo as well as Bar2 (self), in the function f's definition. Firstly, since it is a mutable reference, it can't convert Bar2<'a> to Bar2<'x>, because &mut Bar2<'a> and &mut Bar2<'x> are invariant with each other. (remember even if T < U or T > U, then &mut T is invariant with &mut U). So the compiler has to go with Bar2<'a> and secondly, since the function f is having the same lifetimes for references to Bar2 and Foo, can't convert &'a Bar2<'a> to &'x Bar2<'a>. So it means the references aren't "shortened" when calling the function f and they will remain valid till the end of the block.
If self's lifetime is elided, then the compiler will give a fresh lifetime to the self (disjoint with 'b), which means it is free to "temporarily shorten" the life of Bar2 and then pass it's mut reference to f. i.e It will do &'a mut Bar2<'a> to &'x mut Bar2<'a> and then pass it to f. (remember &'lifetime mut is covariant over 'lifetime) and hence it will work.

Resources