I will consider why this code does not work.
TL DR: Invariance in the lifetime of types C<'l> and D<'l> , and the use of one parameter lifetime ( 'l ) for them leads to the fact that the variables of these types retain their values ββas long as the variable b exists, but the variable c (borrowed d ) is discarded to b .
The borrowing controller is essentially a constraint solver. He searches for the shortest lifetimes 0 that satisfy various constraints: the link should not live longer than the value that it refers, the lifetimes must obey the restrictions indicated in the signatures and types of functions, and the lifetimes must obey the dispersion rules 1 .
0 . The shortest link lifetime is better, because then the link does not take longer than necessary.
1 . Rust has a concept of variance that defines whether a value with a longer lifetime can be used in a place that expects a value of a shorter lifetime. The Rustonomicon link explains this in detail.
The code below is a simplified version of the code in question, and it fails with the same error: c does not live long enough. Blocks are marked by the lifetime of the variables. 'a is the lifetime of the variable a , etc. These lifetimes are determined by the structure of the code, and they are fixed.
Times in annotations like ( B(&'ar A) -> B<'ar> etc.) are variable. The loan verification tool attempts to find reliable fixed-life assignments ( 'a , 'b , 'c ,' d ) for these variables.
The comments below let statements show lifetime limits, which I will explain below.
struct A; struct B<'l>(&'l mut A); struct C<'l>(&'l mut B<'l>); struct D<'l>(&'l mut C<'l>); fn main() { // lifetime 'a let mut a = A; { // lifetime 'b // B(&'r mut A) -> B<'ar> let mut b = B(&mut a); // 'r >= 'ar & 'r <= 'a { // lifetime 'c // C(&'br mut B<'ar>) -> C<'abr> let mut c = C(&mut b); // 'br <= 'b & 'abr = 'ar & 'br >= 'abr { // lifetime 'd // D(&'cr mut C<'abr>) -> D<'cabr> let d = D(&mut c); // 'cr <= 'c & 'cabr = 'abr & 'cr >= 'cabr } } } }
First appointment
// B(&'r mut A) -> B<'ar> let mut b = B(&mut a); // 'r <= 'a & 'r >= 'ar
A reference to a cannot survive a , hence 'r <= 'a .
&'r mut A is an option over' r, so we can pass it to a constructor of type B<'ar> , which expects &'ar mut A iff 'r >= 'ar .
Second appointment
// C(&'br mut B<'ar>) -> C<'abr> let mut c = C(&mut b); // 'br <= 'b & 'abr = 'ar & 'br >= 'abr
A link cannot survive b ( 'br <= 'b ), &mut B is invariant with respect to b ( 'abr = 'ar ), &'br mut B is an option over 'br ( 'br >= 'abr )
d appointment similar to c .
Rust does not seem to consider lifetimes that it has not yet met as possible destinations. Possible assignments for 'ar are 'a or 'b , for 'abr - 'a , 'b or 'c , etc.
This set of constraints comes down to 'ar = 'abr = 'cabr , and the smallest allowed assignment for 'ar is 'b . Therefore, types b , c and d : B<'b> , C<'b> , D<'b> . That is, the variable d contains a reference to c for the lifetime of 'b , but c discarded at the end of 'c the lifetime.
If we delete d , then c still saves the b borrowed to the end of the service life of 'b , but this is not a problem because b does not survive the lifetime of 'b .
This description is still simplified. For example, if type c is equal to C<'b> , c does not occupy b over the whole life of 'b , it will borrow it for part 'b , starting with the definition of c , but I still do not have a clear understanding.