Improve description again
-- update summary based on review -- rewrite explanation to be more clear and correct
This commit is contained in:
parent
231e3a0415
commit
dd7c48e529
3 changed files with 47 additions and 41 deletions
|
@ -1,5 +1,5 @@
|
|||
This error occurs when there is insufficient information for the rust compiler
|
||||
to prove that a type has a long enough lifetime.
|
||||
This error occurs when there is an unsatisfied outlives bound on a generic
|
||||
type parameter or associated type.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
|
@ -13,58 +13,63 @@ trait NestedBorrowMut<U, V> {
|
|||
impl<T, U, V> NestedBorrowMut<U, V> for T
|
||||
where
|
||||
T: BorrowMut<U>,
|
||||
U: BorrowMut<V>, // error: missing lifetime specifier
|
||||
U: BorrowMut<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
|
||||
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`.
|
||||
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`.
|
||||
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
|
||||
the same lifetime to the input and output.
|
||||
|
||||
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`.
|
||||
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`)
|
||||
|
||||
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.
|
||||
The `borrow_mut()` method implicitly requires that that the input and output
|
||||
have the same lifetime bounds. Thus:
|
||||
|
||||
```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;
|
||||
|
||||
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
|
||||
where
|
||||
T: BorrowMut<U>,
|
||||
U: BorrowMut<V> + 'a, // Adding lifetime specifier
|
||||
U: BorrowMut<V> + 'a,
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue