Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.