I take this to mean you understand borrowing forever is bad, but are asking about something else going on in the code.
I'm not sure where the core confusion is coming in, so I'll cover a couple things.
Expand to see the error
error[E0597]: `person` does not live long enough
--> src/lib.rs:19:18
|
16 | fn example<'a>(mut person: Person<'a>) {
| -- ---------- binding `person` declared here
| |
| lifetime `'a` defined here
...
19 | let _three = person.sur_mut();
| ^^^^^^----------
| |
| borrowed value does not live long enough
| argument requires that `person` is borrowed for `'a`
20 | }
| - `person` dropped here while still borrowed
That's not an error about "borrowing forever" per se, it's an error about borrowing a local for a lifetime longer than the function body. It happens because person is a local, and you would have to create a &'a mut Person<'a> to call person.sur_mut(), and 'a is longer than the function body (as all nameable lifetimes are).
Why didn't it error for person.given()? Variance. You create a &'_ Person<'_>, and the compiler determines that the two lifetimes have to be the same (due to the method call signature), but they otherwise have no constraints (other than being active during the call). Effectively you create a &'x Person<'x> and call Person::<'x>::given with it, where 'x is some lifetime that only lasts as long as the call. This is possible because &'r Person<'p> is covariant in both 'r and 'p.
Why didn't the same thing happen with sur_mut? Because &mut T is invariant in T. When you create a &'_ mut Person<'_>, the inner lifetime cannot be coerced to something shorter, so it must be a &'_ mut Person<'a>. Then the signature of the method you pass it to says the lifetimes must be the same, so it must be a &'a mut Person<'a>.
Because Person<'p> is covariant in 'p, if you provide an opportunity to coerce person to a Person<'some_shorter_lifetime> before creating the &mut Person<'_>...
fn example<'a>(mut person: Person<'a>) {
+ let mut person = person;
let _one = person.given();
let _two = person.given();
let _three = person.sur_mut();
}
...the error about borrowing a local too long goes away (but you would still get an error if you tried to use person after the call to sur_mut, because borrowing forever).
Reborrows
In the snippet, the body of sur_mut returns a reborrow of *person.sur. The lifetime in the type cannot be shorter due to the function signature, but that's not the point of this part of my reply.
The point I will try to make is that _three does not act the same as if you had done the reborrow inline. You can have a function like so:
fn deconstruct<'a>(person: Person<'a>) -> &'a mut str {
let reborrow: &'a mut str = &mut *person.sur;
reborrow
}
And it will compile. So reborrow is not a borrow of person.sur or of person -- those both went out of scope at the end of deconstruct. It's a (re-)borrow of *person.sur.
There are still checks that the reborrow can't have a lifetime longer than that of person.sur, that person.sur and person as a whole aren't usable while the exclusive reborrow exists, and so on. But the fact that these other paths are not being borrowed themselves is a key part of how reborrows work.
And there is no way to make a method signature that works the same way, where you take some outer path but only reborrow some inner path. Even though the body of sur_mut is returning a reborrow, person remains exclusively borrowed for 'a. Why? Because you had to create &'a mut Person<'a> to call the method in the first place. And the creation of that outer reference itself is the problem. (This is basically what @jonh said, in long form.)
This change to the OP compiles:
fn example<'a>(mut person: Person<'a>) {
let _one = person.given();
let _two = person.given();
- let _three = person.sur_mut();
+ let _three: &'a mut str = &mut *person.sur;
}
Due to the differences between creating the reborrow inline and calling the method.
-
With a reborrow: person and person.sur are not themselves borrowed; you did not have to create a &mut Person<'_> or &mut &mut str.
-
With the method call: you must create a &'a mut Person<'a>, which leads to the "local borrowed too long" error.