Lifetimes & References
What a Lifetime Is
A lifetime is a scope during which a reference is valid. When a function returns a reference, Rust must know which input that reference could point into; the answer is the overlap of the input lifetimes:
Figure 1 — a returned &'a cannot outlive the shortest input lifetime.
Lifetime Elision
Most lifetimes are elided: the compiler infers them from a few simple rules, so ordinary functions need no annotations at all. There is no lifetime syntax here, yet the code is fully checked:
fn first_word(s: &str) -> &str { // lifetimes inferred
s.split_whitespace().next().unwrap_or("")
}
fn main() {
let text = String::from("hello world");
let w = first_word(&text);
println!("{w}");
}
Explicit Annotations
When a function takes two references and returns one, the compiler can no longer guess which input the output borrows from — so you annotate. The signature below says "the returned reference lives as long as the shorter of the inputs":
// both inputs and the output share lifetime 'a
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() { x } else { y }
}
fn main() {
let a = String::from("longer");
let b = String::from("short");
let result = longest(&a, &b);
println!("longest: {result}");
}
Dangling References Are Prevented
The payoff is that returning a reference to data that is about to be dropped simply does not compile. These are the two classic failures the compiler catches:
fn main() {
let _result;
{
let temp = String::from("soon gone");
// _result = &temp; // ERROR: `temp` does not live long enough
}
// println!("{_result}"); // would be a dangling reference
}
// fn broken() -> &str {
// let local = String::from("gone");
// &local // ERROR: cannot return reference to local
// }