-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Code
pub struct Foo {
}
pub struct Bar<'f> {
pub foo: &'f mut Foo,
}
pub struct Quux<'b, 'f> {
pub bar: &'b mut Bar<'f>,
}
impl<'f> Bar<'f> {
pub fn quux<'b>(&'b mut self) -> Quux {
Quux { bar: self }
}
}
#[test]
fn test_bar() {
let mut foo = Foo {};
let mut bar = Bar { foo: &mut foo };
let quux = bar.quux();
drop(quux);
let _quux2 = bar.quux();
}
Current output
error: lifetime may not live long enough
--> src/lib.rs:12:9
|
10 | impl<'f> Bar<'f> {
| -- lifetime `'f` defined here
11 | pub fn quux<'b>(&'b mut self) -> Quux {
| -- lifetime `'b` defined here
12 | Quux { bar: self }
| ^^^^^^^^^^^^^^^^^^ method was supposed to return data with lifetime `'f` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'f`
= note: requirement occurs because of the type `Quux<'_, '_>`, which makes the generic argument `'_` invariant
= note: the struct `Quux<'b, 'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
Desired output
help: consider annotating the return type to satisfy the necessary bounds: `Quux<'_, 'f>`
Rationale and extra context
The suggestion "consider adding the following bound: 'b: 'f
" is incorrect in this case. That gets me back to the original error I was trying to solve (which is "error[E0499]: cannot borrow bar
as mutable more than once at a time").
The correct suggestion is that the return type needs to be explicitly annotated:
pub fn quux<'b>(&'b mut self) -> Quux<'b, 'f> {
(In this case, it's actually sufficient to just annotate it Quux<'_, 'f>
.)
To be honest, I don't understand what the compiler means when it says "type Quux<'_, '_>
, which makes the generic argument '_
invariant". Maybe if I understood that, I might have been able to see the correct solution sooner. But even if I had, the "help" suggestion that the compiler suggests is not a correct fix here.
Other cases
Adding/removing a lifetime annotation here or there can easily bring you to wildly different errors, none of which teach you the correct solution.
My original definition for Quux
was
pub struct Quux<'a> {
pub bar: &'a mut Bar<'a>,
}
After a long time, I figured out that this doesn't work because it effectively tells the compiler "as long as Bar
lives, so must Quux
", which leads to a contradiction. That led me to put two lifetimes on Quux
, but that change by itself doesn't fix the problem.
If you do
impl<'f> Bar<'f> {
pub fn quux(&mut self) -> Quux {
Quux { bar: self }
}
}
then the error you get is
error: lifetime may not live long enough
--> src/lib.rs:12:9
|
10 | impl<'f> Bar<'f> {
| -- lifetime `'f` defined here
11 | pub fn quux(&mut self) -> Quux {
| - let's call the lifetime of this reference `'1`
12 | Quux { bar: self }
| ^^^^^^^^^^^^^^^^^^ method was supposed to return data with lifetime `'f` but it is returning data with lifetime `'1`
|
= note: requirement occurs because of the type `Quux<'_, '_>`, which makes the generic argument `'_` invariant
= note: the struct `Quux<'b, 'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
This message doesn't provide any suggestion for how to change it. By pointing to the &mut self
parameter, I thought the fix was to annotate the lifetime as &'f mut self
. (Yes, that's the wrong lifetime, but when I only had a single lifetime named 'a
, there was only the one choice for what lifetime to put if it wanted an explicit lifetime.)
Doing that, however, yields this messages, which is what originally got me into this mess:
error[E0499]: cannot borrow `bar` as mutable more than once at a time
--> src/lib.rs:22:18
|
20 | let quux = bar.quux();
| ---------- first mutable borrow occurs here
21 | drop(quux);
22 | let _quux2 = bar.quux();
| ^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
This was extremely confusing, because it wasn't apparent why bar
must live that long. Why does doing bar.quux()
a second time use the first borrow at all?
If you change it to something obviously wrong, like
let quux = bar.quux();
let _quux2 = bar.quux();
drop(quux);
then you get
error[E0499]: cannot borrow `bar` as mutable more than once at a time
--> src/lib.rs:21:18
|
20 | let quux = bar.quux();
| ---------- first mutable borrow occurs here
21 | let _quux2 = bar.quux();
| ^^^^^^^^^^ second mutable borrow occurs here
22 | drop(quux);
| ---- first borrow later used here
which is definitely clearer, and fairly obvious why it's incorrect. But the former message doesn't explain at all why the second bar.quux()
would need both borrows to be alive at the same time.
For a while I thought there might be something magical about self
that was affecting the lifetime. I tried implementing equivalent functions outside of a struct using ordinary parameters:
fn get_quux<'b, 'f>(bar: &'b mut Bar<'f>) -> Quux {
Quux { bar }
}
#[test]
fn test_bar_no_struct() {
let mut foo = Foo {};
let mut bar = Bar { foo: &mut foo };
let quux = get_quux(&mut bar);
drop(quux);
let quux2 = get_quux(&mut bar);
}
This gives a completely different error, which is much more helpful!
error[E0106]: missing lifetime specifiers
--> src/lib.rs:1129:46
|
1129 | fn get_quux<'b, 'f>(bar: &'b mut Bar<'f>) -> Quux {
| --------------- ^^^^ expected 2 lifetime parameters
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
note: these named lifetimes are available to use
--> src/lib.rs:1129:13
|
1129 | fn get_quux<'b, 'f>(bar: &'b mut Bar<'f>) -> Quux {
| ^^ ^^
help: consider using one of the available lifetimes here
|
1129 | fn get_quux<'b, 'f>(bar: &'b mut Bar<'f>) -> Quux<'lifetime, 'lifetime> {
| ++++++++++++++++++++++
Anything else?
No response