Detailed explanation
Your problem is quite interesting and it's certainly hard to understand directly why it doesn't work. It helps a lot if you understand how the compiler does unification. We will walk through all steps the compiler does in order to find out types.
In order to make it a bit easier, we use this simplified example:
let vincent = Officer::new(8);
let mut john = Officer::new(5);
john.set_next(&vincent);
This results in the same error message:
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, let's transform the code in a form that's more explicit, lifetime wise:
{ // 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're prepared to see what the compiler is thinking, step by step:
{ // start 'v
let vincent = Officer::new(8); // : Officer<'?arg_vincent>
Rust doesn't know the lifetime parameter yet, thus it can only deduce an incomplete type here. Hopefully we can fill out the details later! When the compiler wants to show missing type information, it prints an underscore (e.g. Vec<_>
). In this example I've written the missing information as '?arg_vincent
. That way we can refer to it later.
{ // start 'j
let mut john = Officer::new(5); // : Officer<'?arg_john>
The same as above.
john.set_next(&vincent);
Now it get's interesting! The compiler has this function signature:
fn set_next(&'a mut self, next: &'a Policeman<'a>)
Now, the compiler's job is to find a fitting lifetime 'a
that satisfies a bunch of conditions:
- We have
&'a mut self
and john
is self
here. So 'a
can't live longer than john
. In other words: 'j
outlives 'a
, denoted 'j: 'a
.
- We have
next: &'a ...
and next
is vincent
, so (just like above), 'a
can't live longer than vincent
. 'v
outlives 'a
=> 'v: 'a`.
- Lastly, the
'a
in Policeman<'a>
refers to the (yet to be determined) lifetime parameter '?arg_vincent
(since that's what we pass as argument). But '?arg_vincent
is not yet fixed and totally unbounded. So this doesn't impose a restriction on 'a
(unlike the previous two points). Instead, our choice for 'a
determines'?arg_vincent
later: '?arg_vincent := 'a
.
So in short:
'j: 'a and
'v: 'a
So what's a lifetime which lives at most as long as john and as most as long as vincent? 'v
isn't sufficient, since it outlives john
. 'j
is fine; it satisfied the conditions above.
So everything is fine? No! We chose the lifetime 'a = 'j
now. Thus we also know that '?arg_vincent = 'j
! So the full type of vincent
is Officer<'j>
. This in turn tells the compiler that vincent
borrowed something with the lifetime j
. But vincent
lives longer than 'j
, so it outlives its borrow! That's bad. That's why the compiler complains.
This whole thing is really rather complex, and I guess that after reading my explanation, most people feel exactly like I feel after reading most math proofs: each step made sense, but the result isn't intuitive. Maybe this improves the situation slightly:
Since the set_next()
function requires all lifetimes to be 'a
, we impose a lot of restrictions on all lifetimes in our program. This quickly leads to contradictions in restrictions, as it happened here.
A quick fix for my small example
... is to remove the 'a
from the self
parameter:
fn set_next(&mut self, next: &'a Policeman<'a>)
By doing that we remove unnecessary restrictions. Unfortunately, this isn't enough to make your whole example compile.
A more general solution
I'm not very familiar with the design pattern you mentions, but from the looks of it, it's close to impossible to track the involved lifetimes at compile time. Thus I'd use Rc
or Arc
instead of references. With those smartpointers, you don't need to annotate lifetimes and everything "just works". Only disadvantage: a tiny bit of runtime cost.
But it's impossible to tell you the best solution: it really depends on the problem at hand.