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 { impl NestedBorrowMut for T where T: BorrowMut, - U: BorrowMut, // error: missing lifetime specifier + U: BorrowMut, { 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 + '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: BorrowMut + 'a, // Adding lifetime specifier + U: BorrowMut + '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 } } ``` diff --git a/src/test/ui/error-codes/E0311.rs b/src/test/ui/error-codes/E0311.rs index eb9a473e9a2..95f8602306c 100644 --- a/src/test/ui/error-codes/E0311.rs +++ b/src/test/ui/error-codes/E0311.rs @@ -11,7 +11,8 @@ where { 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 } } diff --git a/src/test/ui/error-codes/E0311.stderr b/src/test/ui/error-codes/E0311.stderr index a219a6352ad..2cf6404f2f1 100644 --- a/src/test/ui/error-codes/E0311.stderr +++ b/src/test/ui/error-codes/E0311.stderr @@ -20,10 +20,10 @@ LL | U: BorrowMut + '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 + 'a, // Error is caused by missing lifetime here