#8 Lifetimes
'a, lifetime annotations
Why lifetimes?
Every reference in Rust has a lifetime — the scope for which that reference is valid. Most of the time, lifetimes are inferred automatically, just like types. But sometimes the compiler needs your help to understand how long references should live.
// This does NOT compile — which reference should we return?
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
// Error: missing lifetime specifierThe compiler cannot determine whether the returned reference comes from x or y, so it does not know how long the return value will be valid. Lifetime annotations solve this.
Lifetime annotations
Lifetime annotations describe the relationships between the lifetimes of references. They start with an apostrophe ('a):
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let s1 = String::from("hello");
let result;
{
let s2 = String::from("world!");
result = longest(&s1, &s2);
println!("Longest: {result}");
}
}The annotation 'a tells the compiler: the returned reference will be valid for the shorter of the two input lifetimes. It does not change how long values live — it helps the compiler verify that references are used safely.
Lifetime elision
Rust has lifetime elision rules that let you omit annotations in common cases. The compiler applies these rules automatically:
// Rule 1: Each reference parameter gets its own lifetime
// Rule 2: If there's exactly one input lifetime, it's assigned to all outputs
// Rule 3: If one parameter is &self or &mut self, its lifetime is used
// You write:
fn first_word(s: &str) -> &str { /* ... */ }
// The compiler sees:
fn first_word<'a>(s: &'a str) -> &'a str { /* ... */ }
// You write:
fn len(s: &str) -> usize { s.len() }
// No output reference, so no lifetime needed on return typeThanks to elision, you rarely need to write lifetime annotations. The compiler will tell you when it needs them.
Lifetimes in structs
When a struct holds a reference, it needs a lifetime annotation to ensure the referenced data lives at least as long as the struct:
struct Excerpt<'a> {
text: &'a str,
}
impl<'a> Excerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return(&self, announcement: &str) -> &str {
println!("Announcement: {announcement}");
self.text
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().unwrap();
let excerpt = Excerpt { text: first_sentence };
println!("Excerpt: {}", excerpt.text);
}The 'a annotation means: the Excerpt struct cannot outlive the string it references. The compiler enforces this at every call site.
The 'static lifetime
The special lifetime 'static means the reference lives for the entire program. String literals have this lifetime:
let s: &'static str = "I live forever!";
// String literals are stored in the binary,
// so they're always valid for the program's duration.Your turn
Try running the lifetimes demo with cargo run and cargo check in the terminal below: