Rust Lifetimes
A lifetime is the scope during which a reference stays valid. Rust tracks lifetimes to guarantee that references never outlive the data they point to. Most of the time, Rust figures out lifetimes automatically. You need to write them explicitly only in situations where Rust cannot determine them on its own.
The Problem Lifetimes Solve
A dangling reference is a reference that points to data that has already been freed. Lifetimes make dangling references impossible.
let reference;
{
let data = String::from("hello");
reference = &data; ← data lives only inside this block
}
println!("{}", reference); ← Compiler error: data dropped before use
The compiler sees that data is dropped at the end of the inner block while reference is still being used outside. It rejects this code with a lifetime error.
The Expiry Date Diagram
data expires at } ←─────────────────────────┐
reference points to data │
but data expires │
before reference is used ──┘
Rust: "The reference will outlive the value. Rejected."
Lifetime Annotations
Lifetime annotations use an apostrophe followed by a name: 'a, 'b. They tell the compiler how the lifetimes of different references relate to each other.
Consider a function that takes two string slices and returns the longer one:
fn longest(x: &str, y: &str) -> &str { ← Compiler error: missing lifetime
if x.len() > y.len() { x } else { y }
}
The compiler cannot tell whether the returned reference lives as long as x or as long as y. You add a lifetime annotation to explain:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
The annotation 'a says: the returned reference lives at least as long as the shorter of the two input lifetimes. The function does not change how long data lives — it only tells the compiler what guarantees already exist.
Using the longest Function
fn main() {
let s1 = String::from("long string");
let result;
{
let s2 = String::from("xyz");
result = longest(s1.as_str(), s2.as_str());
println!("Longest: {}", result); ← Works: result used here
}
}
Lifetime Annotations in Structs
When a struct holds a reference, you must annotate the struct's lifetime to show how long the reference is valid:
struct Quote<'a> {
text: &'a str,
}
impl<'a> Quote<'a> {
fn display(&self) {
println!("Quote: {}", self.text);
}
}
fn main() {
let book_text = String::from("To be or not to be.");
let q = Quote { text: &book_text };
q.display();
}
Lifetime Elision Rules
Rust has rules for common patterns that let you skip writing lifetime annotations in many cases. These are called elision rules. The compiler applies them automatically:
fn first_word(s: &str) -> &str { ... }
The compiler infers that the output reference lives as long as the input reference. You do not need to write 'a here.
Elision applies when there is exactly one input reference, or when a method takes &self. In all other cases, you must annotate explicitly.
The 'static Lifetime
The 'static lifetime means the reference lives for the entire duration of the program. String literals have 'static lifetime because they are stored in the binary:
let s: &'static str = "I live for the whole program.";
Quick Reference
fn f<'a>(x: &'a str) -> &'a str ← Output lives as long as input
struct S<'a> { field: &'a str } ← Struct holds a reference
'static ← Lives for entire program
