Review updates: simpler MWE and docs

- use the simpler minimum working example from the review
- add an alterate "fix" that helps make the cause of the error more
  clear
- attempt to add an improved description of what is going on
This commit is contained in:
Matthew Kelly 2022-08-26 15:01:49 -04:00
parent 4f194a76c8
commit de3e95b56c
3 changed files with 54 additions and 102 deletions

View File

@ -1,69 +1,52 @@
This error occurs when there is an unsatisfied outlives bound on a generic
type parameter or associated type.
This error occurs when there is an unsatisfied outlives bound involving an
elided region on a generic type parameter or associated type.
Erroneous code example:
```compile_fail,E0311
use std::borrow::BorrowMut;
trait NestedBorrowMut<U, V> {
fn nested_borrow_mut(&mut self) -> &mut V;
fn no_restriction<T>(x: &()) -> &() {
with_restriction::<T>(x)
}
impl<T, U, V> NestedBorrowMut<U, V> for T
where
T: BorrowMut<U>,
U: BorrowMut<V>,
{
fn nested_borrow_mut(&mut self) -> &mut V {
let u_ref = self.borrow_mut();
let v_ref = u_ref.borrow_mut();
v_ref
}
fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () {
x
}
```
Why doesn't this code compile? It helps to look at the lifetime bounds that
the compiler is automatically adding ("Lifetime Ellision", Chapter 10.3 in the
Rust book) to the `nested_borrow_mut()` and `borrow_mut()` functions. In both
cases the input is a reference to `self`, so the compiler attempts to assign
the same lifetime to the input and output.
Why doesn't this code compile? It helps to look at the lifetime bounds that are
automatically adding by the compiler. For more details see the Rust
Documentation for Lifetime Elision:
https://doc.rust-lang.org/reference/lifetime-elision.html]
Looking specifically at `nested_borrow_mut()`, we see that there are three
object references to keep track of, along with their associated lifetimes:
- `self` (which is a `&mut T`)
- `u_ref` (which is a `&mut U`)
- `v_ref` (which is a `&mut V`)
There are two lifetimes being passed into the `no_restriction()` function: one
associated with the generic type `T` parameter and the other with the input
argument `x`. The compiler does not know which of these lifetimes can be
assigned to the output reference, so we get an error.
The `borrow_mut()` method implicitly requires that that the input and output
have the same lifetime bounds. Thus the lines: `let u_ref = self.borrow_mut();`
and `let v_ref = u_ref.borrow_mut();` in `nested_borrow_mut()` above imply that
`u_ref` and `self` must share a lifetime bound, and also that `v_ref` and
`u_ref` share a lifetime bound. The problem is that the function signature for
`nested_borrow_mut()` only gives the compiler information about the lifetimes
of `self` and `v_ref` -- nothing about `u_ref`.
One way to "fix" this code would be to remove the generic type argument `T`.
In this case, the lifetime elision works because there is a single input
lifetime, which is associated with `x`.
The way to fix this error is then to explicitly tell the compiler that the
lifetime of `u_ref` is the same as `self` and `v_ref`, which then allows it
to satisfy the two lifetime bound requirements described above.
Here is the working version of the code:
```
use std::borrow::BorrowMut;
trait NestedBorrowMut<'a, U, V> {
fn nested_borrow_mut(&'a mut self) -> &'a mut V;
fn no_restriction(x: &()) -> &() {
with_restriction(x)
}
impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T
where
T: BorrowMut<U>,
U: BorrowMut<V> + 'a,
{
fn nested_borrow_mut(&'a mut self) -> &'a mut V {
let u_ref = self.borrow_mut();
let v_ref = u_ref.borrow_mut();
v_ref
}
fn with_restriction<'a>(x: &'a ()) -> &'a () {
x
}
```
The "correct" way to resolve this error is to explicitly tell the compiler
which input lifetime should be assigned to the output. In this case we give
both the generic type `T` parameter and the argument `x` the same lifetime
requirement as the output reference, producing a working version of the code:
```
fn no_restriction<'a, T: 'a>(x: &'a ()) -> &'a () {
with_restriction::<T>(x)
}
fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () {
x
}
```

View File

@ -1,19 +1,9 @@
use std::borrow::BorrowMut;
trait NestedBorrowMut<U, V> {
fn nested_borrow_mut(&mut self) -> &mut V;
fn no_restriction<T>(x: &()) -> &() {
with_restriction::<T>(x) //~ ERROR E0311
}
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
let v_ref = u_ref.borrow_mut(); //~ ERROR E0311
v_ref
}
fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () {
x
}
fn main() {}

View File

@ -1,45 +1,24 @@
error[E0311]: the parameter type `U` may not live long enough
--> $DIR/E0311.rs:13:21
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/E0311.rs:2:5
|
LL | let u_ref = self.borrow_mut();
| ^^^^^^^^^^^^^^^^^
LL | with_restriction::<T>(x)
| ^^^^^^^^^^^^^^^^^^^^^
|
note: the parameter type `U` must be valid for the anonymous lifetime defined here...
--> $DIR/E0311.rs:12:26
note: the parameter type `T` must be valid for the anonymous lifetime defined here...
--> $DIR/E0311.rs:1:25
|
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 | fn no_restriction<T>(x: &()) -> &() {
| ^^^
note: ...so that the type `T` will meet its required lifetime bounds
--> $DIR/E0311.rs:2:5
|
LL | let u_ref = self.borrow_mut();
| ^^^^^^^^^^^^^^^^^
LL | with_restriction::<T>(x)
| ^^^^^^^^^^^^^^^^^^^^^
help: consider adding an explicit lifetime bound...
|
LL | U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here
| ++++
LL | fn no_restriction<T: 'a>(x: &()) -> &() {
| ++++
error[E0311]: the parameter type `U` may not live long enough
--> $DIR/E0311.rs:14:21
|
LL | let v_ref = 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:21
|
LL | let v_ref = 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
error: aborting due to previous error
For more information about this error, try `rustc --explain E0311`.