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

View File

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