According to The Rust Book, lifetime elision allows we to have this signature:
fn first_word(s: &str) -> &str {
...
}
Instead of this more verbose version:
fn first_word<'a>(s: &'a str) -> &'a str {
...
}
But later in the same book, I found that I can't write this:
fn make_a_cloner(s: &str) -> impl Fn() -> String {
move || s.to_string()
}
I have to give the return type a lifetime:
fn make_a_cloner(s: &str) -> impl Fn() -> String + '_ {
...
}
I'm a bit confused. Why don't we need to specify any lifetime when we return a &str
, but we need to add '_
when we return a impl Fn() -> String
? Why doesn't the same elision rule apply in both cases?
As much as lifetime elision can be obscuring, Rust's syntax does want it to be clear when borrows occur. References are one such place that is a clear borrower and even for structs with lifetimes you are encouraged to use MyStruct<'_>
for arguments and parameters so that the lifetime is obvious. There is a elided_lifetimes_in_paths
lint in the compiler to help with this, but is unfortunately allowed by default; I encourage you to add it in your own code.
However, when you return a impl Trait
(with no lifetime) then there is no annotation to convey that a borrow has occurred. Adding the placeholder lifetime + '_
at least communicates to the compiler and other developers that a borrow is happening. As such, without a lifetime annotation returning a impl Trait
will use 'static
by default unless there is some other part of the trait's type that conveys a borrow (i.e. impl Trait<&'a T>
will be bound by 'a
, not 'static
).
This does not necessarily apply to impl Trait
in other places (like function parameters).