]> git.lizzy.rs Git - rust.git/commitdiff
Improve description again
authorMatthew Kelly <matthew.kelly2@gmail.com>
Thu, 25 Aug 2022 00:44:09 +0000 (20:44 -0400)
committerMatthew Kelly <matthew.kelly2@gmail.com>
Thu, 25 Aug 2022 00:44:09 +0000 (20:44 -0400)
-- update summary based on review
-- rewrite explanation to be more clear and correct

compiler/rustc_error_codes/src/error_codes/E0311.md
src/test/ui/error-codes/E0311.rs
src/test/ui/error-codes/E0311.stderr

index e73d5f16f9bf39fb2849b060fdb3505591f4cbd9..a9d44dcdcee82b03436579742fd9be231af1e009 100644 (file)
@@ -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
     }
 }
 ```
index eb9a473e9a2d1e0a8341ee334b1fbdb5af77f807..95f8602306cdec69705b427540660f33ac444e1b 100644 (file)
@@ -11,7 +11,8 @@ impl<T, U, V> NestedBorrowMut<U, V> for T
 {
     fn nested_borrow_mut(&mut self) -> &mut V {
         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
     }
 }
 
index a219a6352adc8a2061ee44bcf3b7e526b4f41c16..2cf6404f2f1c0265eb3dec8d20240884d417c219 100644 (file)
@@ -20,10 +20,10 @@ LL |     U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here
    |                     ++++
 
 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...
   --> $DIR/E0311.rs:12:26
@@ -31,10 +31,10 @@ note: the parameter type `U` must be valid for the anonymous lifetime defined he
 LL |     fn nested_borrow_mut(&mut self) -> &mut V {
    |                          ^^^^^^^^^
 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...
    |
 LL |     U: BorrowMut<V> + 'a, // Error is caused by missing lifetime here