Lifetimes in Rust
Lifetimes are how Rust tracks how long references are valid. The compiler uses them to guarantee you never have a dangling pointer.
The basic idea: every reference has a lifetime - the scope during which it’s valid. Usually the compiler figures this out automatically. When it can’t, you add annotations to help it.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
The 'a says: the returned reference will be valid as long as both inputs are valid. The compiler needs to know this because otherwise it can’t check that you don’t use the result after one of the inputs is gone.
Most of the time you don’t write lifetimes explicitly. Rust has “elision rules” that infer them:
- Each reference parameter gets its own lifetime
- If there’s one input lifetime, output references get that lifetime
- If there’s
&self, output references getself’s lifetime
So fn first_word(s: &str) -> &str works without annotations - the compiler infers the output lives as long as the input.
When you do need explicit lifetimes, it’s usually because:
- Multiple reference parameters could be the source of the return value
- A struct holds references (it needs to know how long they’re valid)
struct Excerpt<'a> {
text: &'a str, // this reference must outlive the struct
}
The mental model: lifetimes aren’t about making references live longer. They’re about proving to the compiler that references already live long enough for how you’re using them.