Lifetimes 101
Every reference has a lifetime — a region of code over which it stays valid.
Almost always inferred. The annotations 'a only appear when the
compiler can't decide on its own. The day you can read function signatures
with lifetime annotations is the day Rust feels native.
1 · The intuition
A lifetime is a region of code. The compiler tracks: "this reference must not
be used outside this region". Most of the time it figures it out via three
elision rules and you never write a 'a in your life. When you do
see one, it's a contract: the returned reference lives at least as long as
the inputs marked with the same lifetime.
2 · Lifetime elision — the three rules
- Each input reference gets its own lifetime.
- If there is exactly one input lifetime, it is assigned to the output.
- If one of the inputs is
&selfor&mut self, that lifetime is assigned to all output references.
Those three rules cover almost every function you'll write. If they don't, the compiler tells you exactly which annotation is missing.
3 · Try it
// 'a says: the returned &str lives at least as long as both inputs.
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() > b.len() { a } else { b }
}
fn main() {
let s1 = String::from("long string here");
let result;
{
let s2 = String::from("short");
result = longest(s1.as_str(), s2.as_str());
println!("longest: {}", result);
}
// println!("{}", result); // ERROR if uncommented — result might reference s2 which is gone
}4 · The 'static lifetime
Special: 'static means "lives for the entire program". String
literals are &'static str — they're baked into the binary.
Reach for 'static when you really do mean "never freed".
let s: &'static str = "this is in the binary";
let n: &'static i32 = &42; // primitives can be static refs too5 · Common mistakes
- Adding
'aannotations when not needed. Elision usually handles it. Let the compiler ask first. - Returning a reference to a local. Doesn't compile. Return owned data instead, or refactor so the caller passes the storage.
- Struct fields that are references. Require lifetime annotation:
struct Foo<'a> { s: &'a str }. Then every method/function usingFoocarries the lifetime. - Mistaking
'staticfor "anywhere is fine". It means "lives forever" — a constraint. Use sparingly.
6 · When it clicks
- You read
fn foo<'a>(x: &'a str) -> &'a stras "the result borrows from x". - Most days you don't write
'aat all — elision handles it. - Struct-with-references is something you reach for deliberately, not by accident.