about summary refs log tree commit diff
path: root/compiler/rustc_error_codes/src
diff options
context:
space:
mode:
authorMatthew Kelly <matthew.kelly2@gmail.com>2022-08-24 20:44:09 -0400
committerMatthew Kelly <matthew.kelly2@gmail.com>2022-08-24 20:44:09 -0400
commitdd7c48e52937dade956379f3faf7a1bff1b95a5f (patch)
tree696f74372b4d034b27b0f9c6997681cbeaca8c18 /compiler/rustc_error_codes/src
parent231e3a041512027b13c87b07fe2a22c89b290adb (diff)
downloadrust-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.md73
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
     }
 }
 ```