-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add long description and test for E0311 #100747
Changes from 6 commits
fa91980
08fa70e
63de1ec
a9cefd0
dbcc409
231e3a0
dd7c48e
fc02eee
4f194a7
de3e95b
deadf07
4a443df
eda2a40
24aab52
0d9c014
c0d32fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,70 @@ | ||||||||
This error occurs when there is insufficient information for the rust compiler | ||||||||
to prove that a type has a long enough lifetime. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
something like this maybe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah - that's much better. |
||||||||
|
||||||||
Erroneous code example: | ||||||||
|
||||||||
```compile_fail,E0311 | ||||||||
use std::borrow::BorrowMut; | ||||||||
|
||||||||
trait NestedBorrowMut<U, V> { | ||||||||
fn nested_borrow_mut(&mut self) -> &mut V; | ||||||||
} | ||||||||
|
||||||||
impl<T, U, V> NestedBorrowMut<U, V> for T | ||||||||
where | ||||||||
T: BorrowMut<U>, | ||||||||
U: BorrowMut<V>, // error: missing lifetime specifier | ||||||||
{ | ||||||||
fn nested_borrow_mut(&mut self) -> &mut V { | ||||||||
self.borrow_mut().borrow_mut() | ||||||||
} | ||||||||
} | ||||||||
``` | ||||||||
|
||||||||
Why doesn't this code compile? The problem has to do with Rust's rules for | ||||||||
lifetime elision in functions (Chapter 10.3 in the Rust book). One of the | ||||||||
inputs is a reference to `self`, so the compiler attempts to assign the | ||||||||
the same lifetime to the `&mut self` input and `&mut V` output to the | ||||||||
`nested_borrow_mut()` function. The problem is that there is no way for the | ||||||||
compiler to directly figure out how these two lifetimes are related in the | ||||||||
implementation of the function. We're implementing the `NextedBorrowMut` | ||||||||
trait for a type `T`, so the `&mut self` reference has the lifetime of `T`. | ||||||||
We know that `T` implements the `BorrowMut` trait returning a reference to `U`, | ||||||||
and that `U` implements the `BorrowMut` trait returning a reference to `V`. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't quite right either: Really what we have is more like:
If we split up the lines in the example we get:
The compiler problem then boils to do failing to prove that Perhaps the right approach here is to copy the failing example above and then add the lifetime annotations that the compiler attempts to add implicitly, similar to how they are shown in the examples in https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I rewrote this to be more clear and correct. |
||||||||
The key is that we have not told the compiler that those two `U` lifetimes | ||||||||
are the same: for all it knows, we could be that the first `BorrowMut` trait | ||||||||
on `T` works with a lifetime `'a` and the second `BorrowMut` trait on `U` | ||||||||
works on a lifetime `'b`. | ||||||||
|
||||||||
The fix here is to add explicit lifetime annotations that tell the compiler | ||||||||
that the lifetime of the output is in fact the same as the lifetime of the | ||||||||
input (`self`). There are three references involved, to objects of type `T` | ||||||||
(`self`), `U` (the intermediate type), and `V` (the return type). In the | ||||||||
working code below, we see that all have been given the same lifetime `'a`: | ||||||||
- `&'a mut self` in the function argument list for `T` | ||||||||
- `U: BorrowMut<V> + 'a` in the trait bounds for `U` | ||||||||
- `&'a mut V` in the function return for `V`. | ||||||||
|
||||||||
The compiler can the check that the implementation of the | ||||||||
`nested_borrow_mut()` function satisfies these lifetimes. There are two | ||||||||
functions being called inside of `nested_borrow_mut()`, both of which are | ||||||||
the `borrow_mut()` function, which promises that the output lifetime is | ||||||||
the same as the input lifetime (see lifetime elision rules), which checks out. | ||||||||
|
||||||||
``` | ||||||||
use std::borrow::BorrowMut; | ||||||||
|
||||||||
trait NestedBorrowMut<'a, U, V> { | ||||||||
fn nested_borrow_mut(& 'a mut self) -> &'a mut V; | ||||||||
} | ||||||||
|
||||||||
impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T | ||||||||
where | ||||||||
T: BorrowMut<U>, | ||||||||
U: BorrowMut<V> + 'a, // Adding lifetime specifier | ||||||||
{ | ||||||||
fn nested_borrow_mut(&'a mut self) -> &'a mut V { | ||||||||
self.borrow_mut().borrow_mut() | ||||||||
} | ||||||||
} | ||||||||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
use std::borrow::BorrowMut; | ||
|
||
trait NestedBorrowMut<U, V> { | ||
fn nested_borrow_mut(&mut self) -> &mut V; | ||
} | ||
|
||
impl<T, U, V> NestedBorrowMut<U, V> for T | ||
where | ||
T: BorrowMut<U>, | ||
U: BorrowMut<V>, // Error is caused by missing lifetime here | ||
{ | ||
fn nested_borrow_mut(&mut self) -> &mut V { | ||
let u_ref = self.borrow_mut(); //~ ERROR E0311 | ||
u_ref.borrow_mut() //~ ERROR E0311 | ||
} | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
error[E0311]: the parameter type `U` may not live long enough | ||
--> $DIR/E0311.rs:13:21 | ||
| | ||
LL | let u_ref = self.borrow_mut(); | ||
| ^^^^^^^^^^^^^^^^^ | ||
| | ||
note: the parameter type `U` must be valid for the anonymous lifetime defined here... | ||
--> $DIR/E0311.rs:12:26 | ||
| | ||
LL | fn nested_borrow_mut(&mut self) -> &mut V { | ||
| ^^^^^^^^^ | ||
note: ...so that the type `U` will meet its required lifetime bounds | ||
--> $DIR/E0311.rs:13:21 | ||
| | ||
LL | let u_ref = self.borrow_mut(); | ||
| ^^^^^^^^^^^^^^^^^ | ||
help: consider adding an explicit lifetime bound... | ||
| | ||
LL | U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here | ||
| ++++ | ||
|
||
error[E0311]: the parameter type `U` may not live long enough | ||
--> $DIR/E0311.rs:14:9 | ||
| | ||
LL | u_ref.borrow_mut() | ||
| ^^^^^^^^^^^^^^^^^^ | ||
| | ||
note: the parameter type `U` must be valid for the anonymous lifetime defined here... | ||
--> $DIR/E0311.rs:12:26 | ||
| | ||
LL | fn nested_borrow_mut(&mut self) -> &mut V { | ||
| ^^^^^^^^^ | ||
note: ...so that the type `U` will meet its required lifetime bounds | ||
--> $DIR/E0311.rs:14:9 | ||
| | ||
LL | u_ref.borrow_mut() | ||
| ^^^^^^^^^^^^^^^^^^ | ||
help: consider adding an explicit lifetime bound... | ||
| | ||
LL | U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here | ||
| ++++ | ||
|
||
error: aborting due to 2 previous errors | ||
|
||
For more information about this error, try `rustc --explain E0311`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that
type
would be more correct asparameter
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.