book
book copied to clipboard
Chapter 10.3. Third elision rule.
- [x] I have checked the latest
main
branch to see if this has already been fixed - [x] I have searched existing issues and pull requests for duplicates
URL to the section(s) of the book with this problem: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
Description of the problem: This has to do with the example provided of the 3rd elision rule in action:
impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
I'm new to Rust. My immediate thought in reading this example was "Oh no, what if the function returns 'announcement'? Assigning self's lifetime to the output reference seems very dangerous here. What if 'announcement' is returned instead?" I was so concerned, I tried it. And was very relieved that the compiler reported an error.
Suggested fix: I would suggest stating that the 3rd elision is making the assumption that self (or some component thereof) will be returned. And although the compiler will in all cases apply the 3rd elision rule to a method signature like the above, the code will not compile if that assumption is violated. This is an important difference from the first two elision rules. I believe they can safely be applied without any particular assumptions about the body of the function. Otherwise put, I don't think either of them could cause code to fail to compile.
I agree with this. I also did some testing to check my understanding of what the book was saying. It seems to me that the statements
The third rule is that, if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters.
and
There are two input lifetimes, so Rust applies the first lifetime elision rule and gives both &self and announcement their own lifetimes. Then, because one of the parameters is &self, the return type gets the lifetime of &self, and all lifetimes have been accounted for.
are wrong. The compiler only assigns the lifetime of self to an output parameter if it is a reference to self. As @smccrossin says, if the example is updated to return announcement, it fails to compile with error: lifetime may not live long enough
.
I think what this section is trying to explain is the distinction between a function that takes two references and returns one of them and a method that takes one reference (or any number) and returns a reference to its own member. Only in the latter case does lifetime elision happen.
Expanding on the example in the book:
struct ImportantExcerpt<'a> {
part: &'a str
}
impl<'a> ImportantExcerpt<'a> {
fn m_announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
// fn f_announce_and_return_part(part: &str, announcement: &str) -> &str {
// println!("Attention please: {}", announcement);
// part
// }
fn main() {}
m_announce_and_return_part
compiles OK, but f_announce_and_return_part
does not:
error[E0106]: missing lifetime specifier
--> /tmp/Anpa02arYR/main.rs:19:66
|
19 | fn f_announce_and_return_part(part: &str, announcement: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `part` or `announcement`
help: consider introducing a named lifetime parameter
|
19 | fn f_announce_and_return_part<'a>(part: &'a str, announcement: &'a str) -> &'a str {
| ++++ ++ ++ ++
It would make more sense to me if it was made clear that lifetime elision happens for a method specifically in the case that the output parameter is a reference to a member of self.