Skip to content

Commit dd7c48e

Browse files
Improve description again
-- update summary based on review -- rewrite explanation to be more clear and correct
1 parent 231e3a0 commit dd7c48e

File tree

3 files changed

+47
-41
lines changed

3 files changed

+47
-41
lines changed
Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
This error occurs when there is insufficient information for the rust compiler
2-
to prove that a type has a long enough lifetime.
1+
This error occurs when there is an unsatisfied outlives bound on a generic
2+
type parameter or associated type.
33

44
Erroneous code example:
55

@@ -13,58 +13,63 @@ trait NestedBorrowMut<U, V> {
1313
impl<T, U, V> NestedBorrowMut<U, V> for T
1414
where
1515
T: BorrowMut<U>,
16-
U: BorrowMut<V>, // error: missing lifetime specifier
16+
U: BorrowMut<V>,
1717
{
1818
fn nested_borrow_mut(&mut self) -> &mut V {
19-
self.borrow_mut().borrow_mut()
19+
let u_ref = self.borrow_mut();
20+
let v_ref = u_ref.borrow_mut();
21+
v_ref
2022
}
2123
}
2224
```
2325

24-
Why doesn't this code compile? The problem has to do with Rust's rules for
25-
lifetime elision in functions (Chapter 10.3 in the Rust book). One of the
26-
inputs is a reference to `self`, so the compiler attempts to assign the
27-
the same lifetime to the `&mut self` input and `&mut V` output to the
28-
`nested_borrow_mut()` function. The problem is that there is no way for the
29-
compiler to directly figure out how these two lifetimes are related in the
30-
implementation of the function. We're implementing the `NextedBorrowMut`
31-
trait for a type `T`, so the `&mut self` reference has the lifetime of `T`.
32-
We know that `T` implements the `BorrowMut` trait returning a reference to `U`,
33-
and that `U` implements the `BorrowMut` trait returning a reference to `V`.
34-
The key is that we have not told the compiler that those two `U` lifetimes
35-
are the same: for all it knows, we could be that the first `BorrowMut` trait
36-
on `T` works with a lifetime `'a` and the second `BorrowMut` trait on `U`
37-
works on a lifetime `'b`.
26+
Why doesn't this code compile? It helps to look at the lifetime bounds that
27+
the compiler is automatically adding ("Lifetime Ellision", Chapter 10.3 in the
28+
Rust book) to the `nested_borrow_mut` and `borrow_mut` functions. In both cases
29+
the input is a reference to `self`, so the compiler attempts to assign the
30+
the same lifetime to the input and output.
3831

39-
The fix here is to add explicit lifetime annotations that tell the compiler
40-
that the lifetime of the output is in fact the same as the lifetime of the
41-
input (`self`). There are three references involved, to objects of type `T`
42-
(`self`), `U` (the intermediate type), and `V` (the return type). In the
43-
working code below, we see that all have been given the same lifetime `'a`:
44-
- `&'a mut self` in the function argument list for `T`
45-
- `U: BorrowMut<V> + 'a` in the trait bounds for `U`
46-
- `&'a mut V` in the function return for `V`.
32+
Looking specifically at `nested_borrow_mut`,
33+
we see that there are three object references to keep track of,
34+
along with their associated lifetimes:
35+
- `self` (which is a `&mut T`)
36+
- `u_ref` (which is a `&mut U`)
37+
- `v_ref` (which is a `&mut V`)
4738

48-
The compiler can the check that the implementation of the
49-
`nested_borrow_mut()` function satisfies these lifetimes. There are two
50-
functions being called inside of `nested_borrow_mut()`, both of which are
51-
the `borrow_mut()` function, which promises that the output lifetime is
52-
the same as the input lifetime (see lifetime elision rules), which checks out.
39+
The `borrow_mut()` method implicitly requires that that the input and output
40+
have the same lifetime bounds. Thus:
5341

42+
```rust
43+
let u_ref = self.borrow_mut();
44+
let v_ref = u_ref.borrow_mut();
5445
```
46+
47+
Imply that `u_ref` and `self` must share a lifetime bound, and also that
48+
`v_ref` and `u_ref` share a lifetime bound. The problem is that the function
49+
signature for `nested_borrow_mut` only gives the compiler information about the
50+
lifetimes of `self` and `v_ref` -- nothing about `u_ref`.
51+
52+
The way to fix this error is then to explicitly tell the compiler that the
53+
lifetime of `u_ref` is the same as `self` and `v_ref`, which then allows it
54+
to satisfy the two lifetime bound requirements described above.
55+
56+
Here is the working version of the code:
57+
```rust
5558
use std::borrow::BorrowMut;
5659

5760
trait NestedBorrowMut<'a, U, V> {
58-
fn nested_borrow_mut(& 'a mut self) -> &'a mut V;
61+
fn nested_borrow_mut(&'a mut self) -> &'a mut V;
5962
}
6063

6164
impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T
6265
where
6366
T: BorrowMut<U>,
64-
U: BorrowMut<V> + 'a, // Adding lifetime specifier
67+
U: BorrowMut<V> + 'a,
6568
{
6669
fn nested_borrow_mut(&'a mut self) -> &'a mut V {
67-
self.borrow_mut().borrow_mut()
70+
let u_ref = self.borrow_mut();
71+
let v_ref = u_ref.borrow_mut();
72+
v_ref
6873
}
6974
}
7075
```

src/test/ui/error-codes/E0311.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ where
1111
{
1212
fn nested_borrow_mut(&mut self) -> &mut V {
1313
let u_ref = self.borrow_mut(); //~ ERROR E0311
14-
u_ref.borrow_mut() //~ ERROR E0311
14+
let v_ref = u_ref.borrow_mut(); //~ ERROR E0311
15+
v_ref
1516
}
1617
}
1718

src/test/ui/error-codes/E0311.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,21 @@ LL | U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here
2020
| ++++
2121

2222
error[E0311]: the parameter type `U` may not live long enough
23-
--> $DIR/E0311.rs:14:9
23+
--> $DIR/E0311.rs:14:21
2424
|
25-
LL | u_ref.borrow_mut()
26-
| ^^^^^^^^^^^^^^^^^^
25+
LL | let v_ref = u_ref.borrow_mut();
26+
| ^^^^^^^^^^^^^^^^^^
2727
|
2828
note: the parameter type `U` must be valid for the anonymous lifetime defined here...
2929
--> $DIR/E0311.rs:12:26
3030
|
3131
LL | fn nested_borrow_mut(&mut self) -> &mut V {
3232
| ^^^^^^^^^
3333
note: ...so that the type `U` will meet its required lifetime bounds
34-
--> $DIR/E0311.rs:14:9
34+
--> $DIR/E0311.rs:14:21
3535
|
36-
LL | u_ref.borrow_mut()
37-
| ^^^^^^^^^^^^^^^^^^
36+
LL | let v_ref = u_ref.borrow_mut();
37+
| ^^^^^^^^^^^^^^^^^^
3838
help: consider adding an explicit lifetime bound...
3939
|
4040
LL | U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here

0 commit comments

Comments
 (0)