When not to name lifetimes
Sometimes newcomers try to solve borrow check errors by making things more generic, which often involves adding lifetimes and naming previously-elided lifetimes:
#![allow(unused)]
fn main() {
struct S; impl S {
fn quz<'a: 'b, 'b>(&'a mut self) -> &'b str { todo!() }
// (The meaning is still "uses of the return value keep `*self` exclusively borrowed"
// due to the `'a: 'b` bound: `'a` must be valid wherever `'b` is.)
}
}
But this doesn’t really permit more lifetimes than this:
#![allow(unused)]
fn main() {
struct S; impl S {
fn quz<'b>(&'b mut self) -> &'b str { todo!() }
}
}
Because in the first example, &'a mut self can coerce to &'b mut self.
And, in fact, you want it to – because you generally don’t want to exclusively
borrow self for any longer than is necessary.
And at this point you can instead utilize lifetime elision and stick with:
#![allow(unused)]
fn main() {
struct S; impl S {
fn quz(&mut self) -> &str { todo!() }
}
}
As covariance and function lifetime elision become more intuitive, you’ll build a feel for when it’s pointless to name lifetimes. Adding superfluous lifetimes like in the first example tends to make understanding borrow errors harder, not easier.