Detailed explanation
Your problem is quite interesting, and it is certainly difficult to understand why it does not work. This helps if you understand how the compiler does unification. We will go through all the steps that the compiler takes to find out the types.
To make this a little easier, we use this simplified example:
let vincent = Officer::new(8); let mut john = Officer::new(5); john.set_next(&vincent);
As a result, the same error message appears:
error[E0597]: `john` does not live long enough --> src/main.rs:26:1 | 25 | john.set_next(&vincent); | ---- borrow occurs here 26 | } | ^ `john` dropped here while still borrowed | = note: values in a scope are dropped in the opposite order they are created
First, we transform the code into a more explicit, time-wise form:
{ // start 'v let vincent = Officer::new(8); { // start 'j let mut john = Officer::new(5); john.set_next(&vincent); } // end 'j } // end 'v
Ok, now we are ready to understand what the compiler thinks, step by step:
{ // start 'v let vincent = Officer::new(8); // : Officer<'?arg_vincent>
Rust does not yet know the lifetime parameter, so an incomplete type can be displayed here. Hope we can fill out the details later! When the compiler wants to show missing type information, it prints an underscore (for example, Vec<_> ). In this example, I wrote the missing information as '?arg_vincent . So we can refer to it later.
{ // start 'j let mut john = Officer::new(5); // : Officer<'?arg_john>
Same as above.
john.set_next(&vincent);
Now it is getting interesting! The compiler has this function:
fn set_next(&'a mut self, next: &'a Policeman<'a>)
Now the compiler’s job should find a suitable lifetime, satisfying a bunch of conditions:
- Here
&'a mut self and john here self . Therefore, 'a cannot live longer than john . In other words: 'j survives 'a , denoted by 'j: 'a . - We have
next: &'a ... and next have vincent , so (as above) 'a cannot live longer than vincent . 'v survives 'a => 'v:' a`. - Finally,
'a in Policeman<'a> refers to the (not yet defined) lifetime parameter '?arg_vincent (since we pass as an argument). But '?arg_vincent has not yet been fixed and is completely unlimited. Thus, this does not impose restrictions on 'a (unlike the previous two points). Instead, our choice for 'a defines '?arg_vincent later: '?arg_vincent := 'a .
Shortly speaking:
'j: 'a and 'v: 'a
So, what is life that lives at best as long as john and no more than vincent? 'v not enough, since it is experiencing john . 'j excellent; It satisfies the above conditions.
So, is everything all right? No! Now we have chosen the lifetime 'a = 'j . So we also know that '?arg_vincent = 'j ! So the full vincent type is Officer<'j> . This, in turn, tells the compiler that vincent borrowed something with a lifetime j . But vincent lives longer than 'j , so he survives his loan! This is bad. This is why the compiler complains.
All this is very complicated, and I think that after reading my explanation, most people feel the way I feel after reading most of the mathematical proofs: each step makes sense, but the result is not intuitive. Perhaps this improves the situation a bit:
Since the set_next() function requires all lifetimes to be 'a , we impose many restrictions on all lifetimes in our program. This quickly leads to contradictions in restrictions, as happened here.
Quick fix for my little example
... is to remove 'a from the self parameter:
fn set_next(&mut self, next: &'a Policeman<'a>)
By doing this, we remove unnecessary restrictions. Unfortunately, this is not enough for your entire example to compile.
More general solution
I am not very familiar with the design pattern that you mentioned, but due to its appearance it is almost impossible to track the involved lifetimes during compilation. That way I would use Rc or Arc instead of links. With these smart controllers, you don’t need to annotate life cycles, and everything just “works.” Only flaw: a tiny bit of runtime.
But it is impossible to tell you the best solution: it really depends on the problem.