1
Fork 0

Improve description again

-- update summary based on review
-- rewrite explanation to be more clear and correct
This commit is contained in:
Matthew Kelly 2022-08-24 20:44:09 -04:00
parent 231e3a0415
commit dd7c48e529
3 changed files with 47 additions and 41 deletions

View file

@ -1,5 +1,5 @@
This error occurs when there is insufficient information for the rust compiler This error occurs when there is an unsatisfied outlives bound on a generic
to prove that a type has a long enough lifetime. type parameter or associated type.
Erroneous code example: Erroneous code example:
@ -13,58 +13,63 @@ trait NestedBorrowMut<U, V> {
impl<T, U, V> NestedBorrowMut<U, V> for T impl<T, U, V> NestedBorrowMut<U, V> for T
where where
T: BorrowMut<U>, T: BorrowMut<U>,
U: BorrowMut<V>, // error: missing lifetime specifier U: BorrowMut<V>,
{ {
fn nested_borrow_mut(&mut self) -> &mut V { fn nested_borrow_mut(&mut self) -> &mut V {
self.borrow_mut().borrow_mut() let u_ref = self.borrow_mut();
let v_ref = u_ref.borrow_mut();
v_ref
} }
} }
``` ```
Why doesn't this code compile? The problem has to do with Rust's rules for Why doesn't this code compile? It helps to look at the lifetime bounds that
lifetime elision in functions (Chapter 10.3 in the Rust book). One of the the compiler is automatically adding ("Lifetime Ellision", Chapter 10.3 in the
inputs is a reference to `self`, so the compiler attempts to assign the Rust book) to the `nested_borrow_mut` and `borrow_mut` functions. In both cases
the same lifetime to the `&mut self` input and `&mut V` output to the the input is a reference to `self`, so the compiler attempts to assign the
`nested_borrow_mut()` function. The problem is that there is no way for the the same lifetime to the input and output.
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`.
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 Looking specifically at `nested_borrow_mut`,
that the lifetime of the output is in fact the same as the lifetime of the we see that there are three object references to keep track of,
input (`self`). There are three references involved, to objects of type `T` along with their associated lifetimes:
(`self`), `U` (the intermediate type), and `V` (the return type). In the - `self` (which is a `&mut T`)
working code below, we see that all have been given the same lifetime `'a`: - `u_ref` (which is a `&mut U`)
- `&'a mut self` in the function argument list for `T` - `v_ref` (which is a `&mut V`)
- `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 The `borrow_mut()` method implicitly requires that that the input and output
`nested_borrow_mut()` function satisfies these lifetimes. There are two have the same lifetime bounds. Thus:
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.
```rust
let u_ref = self.borrow_mut();
let v_ref = u_ref.borrow_mut();
``` ```
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`.
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:
```rust
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
trait NestedBorrowMut<'a, U, V> { trait NestedBorrowMut<'a, U, V> {
fn nested_borrow_mut(& 'a mut self) -> &'a mut V; fn nested_borrow_mut(&'a mut self) -> &'a mut V;
} }
impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T
where where
T: BorrowMut<U>, T: BorrowMut<U>,
U: BorrowMut<V> + 'a, // Adding lifetime specifier U: BorrowMut<V> + 'a,
{ {
fn nested_borrow_mut(&'a mut self) -> &'a mut V { fn nested_borrow_mut(&'a mut self) -> &'a mut V {
self.borrow_mut().borrow_mut() let u_ref = self.borrow_mut();
let v_ref = u_ref.borrow_mut();
v_ref
} }
} }
``` ```

View file

@ -11,7 +11,8 @@ where
{ {
fn nested_borrow_mut(&mut self) -> &mut V { fn nested_borrow_mut(&mut self) -> &mut V {
let u_ref = self.borrow_mut(); //~ ERROR E0311 let u_ref = self.borrow_mut(); //~ ERROR E0311
u_ref.borrow_mut() //~ ERROR E0311 let v_ref = u_ref.borrow_mut(); //~ ERROR E0311
v_ref
} }
} }

View file

@ -20,9 +20,9 @@ LL | U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here
| ++++ | ++++
error[E0311]: the parameter type `U` may not live long enough error[E0311]: the parameter type `U` may not live long enough
--> $DIR/E0311.rs:14:9 --> $DIR/E0311.rs:14:21
| |
LL | u_ref.borrow_mut() LL | let v_ref = u_ref.borrow_mut();
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
| |
note: the parameter type `U` must be valid for the anonymous lifetime defined here... note: the parameter type `U` must be valid for the anonymous lifetime defined here...
@ -31,9 +31,9 @@ note: the parameter type `U` must be valid for the anonymous lifetime defined he
LL | fn nested_borrow_mut(&mut self) -> &mut V { LL | fn nested_borrow_mut(&mut self) -> &mut V {
| ^^^^^^^^^ | ^^^^^^^^^
note: ...so that the type `U` will meet its required lifetime bounds note: ...so that the type `U` will meet its required lifetime bounds
--> $DIR/E0311.rs:14:9 --> $DIR/E0311.rs:14:21
| |
LL | u_ref.borrow_mut() LL | let v_ref = u_ref.borrow_mut();
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
help: consider adding an explicit lifetime bound... help: consider adding an explicit lifetime bound...
| |