Lifetimes
Why Lifetimes Exist
Section titled “Why Lifetimes Exist”Rust’s borrow checker prevents dangling references — references that point to memory that has already been freed. In simple cases the compiler can figure out how long a reference is valid on its own. But when a function takes two references and returns one of them, the compiler needs help: which input does the output borrow from, and for how long?
Lifetime annotations answer that question. They are written as 'a (a tick followed by a short name) inside angle brackets.
Lifetime Annotations in Functions
Section titled “Lifetime Annotations in Functions”Here is a function that takes two string slices and returns the longer one. The return value borrows from one of the inputs — but which one? It depends on the runtime values. The annotation 'a says: “the return value lives at most as long as the shorter of the two inputs.”
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y }}All three references share the same lifetime 'a. The compiler uses this to ensure the returned reference never outlives the data it points to.
Without the annotation, the compiler would reject this function: it cannot determine the return lifetime from the inputs alone.
A Dangling Reference Example
Section titled “A Dangling Reference Example”Lifetime annotations exist precisely to catch this class of bug at compile time:
// This does NOT compile — the compiler prevents the dangling reference.// fn dangle() -> &String {// let s = String::from("hello"); // s is created here// &s // we return a reference to s// } // s is dropped here — the reference would dangleRust rejects the code above before it ever runs.
Lifetime Annotations in Structs
Section titled “Lifetime Annotations in Structs”If a struct holds a reference, you must annotate the lifetime so the compiler knows the struct cannot outlive the data it borrows:
struct Excerpt<'a> { text: &'a str,}This says: “an Excerpt cannot outlive the string it borrows from.” The compiler enforces this guarantee automatically.
The Three Lifetime Elision Rules
Section titled “The Three Lifetime Elision Rules”In many common patterns Rust can infer lifetimes automatically using three elision rules, so you do not need to write annotations every time:
- Each reference parameter gets its own lifetime.
fn foo(x: &str, y: &str)becomesfn foo<'a, 'b>(x: &'a str, y: &'b str). - If there is exactly one input reference, its lifetime is assigned to all output references.
fn foo(x: &str) -> &strbecomesfn foo<'a>(x: &'a str) -> &'a str. - If one of the parameters is
&selfor&mut self, its lifetime is assigned to all output references.
When all three rules apply, no annotation is needed. When they do not fully determine the output lifetime (as in longest), you must annotate explicitly.
// A function with a lifetime annotation:// the returned reference lives at least as long as the shorter of x or y.fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y }}
// A struct that holds a reference — must annotate the lifetime.struct Excerpt<'a> { text: &'a str,}
impl<'a> Excerpt<'a> { fn announce(&self) -> &str { self.text }}
fn main() { let string1 = String::from("long string is long"); let result; { let string2 = String::from("xyz"); result = longest(string1.as_str(), string2.as_str()); println!("longest: {}", result); }
let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().expect("Could not find '.'"); let excerpt = Excerpt { text: first_sentence }; println!("excerpt: {}", excerpt.announce());}Compiling…