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
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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...
|
||||||
|
|
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue