diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index 9477a0b1fb7..00b23c42052 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -21,14 +21,35 @@ where } ``` -In this example we have a trait that borrows some inner data element of type `V` -from an outer type `T`, through an intermediate type `U`. The compiler is unable -to prove that the livetime of `U` is long enough to support the reference. To -fix the issue we can explicitly add lifetime specifiers to the `NestedBorrowMut` -trait, which link the lifetimes of the various data types and allow the code to -compile. +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`. -Working implementation of the `NestedBorrowMut` trait: +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`. + +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. ``` use std::borrow::BorrowMut;