diff options
| author | Matthew Kelly <matthew.kelly2@gmail.com> | 2022-08-24 20:44:09 -0400 |
|---|---|---|
| committer | Matthew Kelly <matthew.kelly2@gmail.com> | 2022-08-24 20:44:09 -0400 |
| commit | dd7c48e52937dade956379f3faf7a1bff1b95a5f (patch) | |
| tree | 696f74372b4d034b27b0f9c6997681cbeaca8c18 /compiler/rustc_error_codes/src | |
| parent | 231e3a041512027b13c87b07fe2a22c89b290adb (diff) | |
| download | rust-dd7c48e52937dade956379f3faf7a1bff1b95a5f.tar.gz rust-dd7c48e52937dade956379f3faf7a1bff1b95a5f.zip | |
Improve description again
-- update summary based on review -- rewrite explanation to be more clear and correct
Diffstat (limited to 'compiler/rustc_error_codes/src')
| -rw-r--r-- | compiler/rustc_error_codes/src/error_codes/E0311.md | 73 |
1 files changed, 39 insertions, 34 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index e73d5f16f9b..a9d44dcdcee 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -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 } } ``` |
