← Rust EnglishChapter 11 of 13

Lifetimes

## Learning Objectives - Understand what lifetimes are - Learn lifetime annotations - Master elision rules - Use 'static lifetime - Avoid dangling references ## What are Lifetimes? Lifetimes ensure references are always valid. The compiler checks that references don't outlive the data they refer to. ```rust fn main() { let r; { let x = 5; r = &x; // Error: x doesn't live long enough } println!("{}", r); } ``` ## Lifetime Annotations ### Syntax ```rust &'a type // Reference with lifetime 'a &'a mut type // Mutable reference with lifetime 'a ``` ### Named Lifetimes ```rust 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("long string"); let result; { let s2 = String::from("xyz"); result = longest(s1.as_str(), s2.as_str()); println!("{}", result); } // result would be invalid here if used } ``` ## Lifetime Elision The compiler infers lifetimes in common cases. ### Elision Rules 1. Each reference parameter gets its own lifetime 2. If there's exactly one input lifetime, it's assigned to output 3. If there's a `&self` or `&mut self`, it's assigned to output ### Examples ```rust // Rule 1: Each parameter gets a lifetime fn foo(x: &str, y: &str) -> &str { } // Compiler sees: foo<'a, 'b>(x: &'a str, y: &'b str) -> &str // Rule 2: Single input lifetime -> output fn first_word(s: &str) -> &str { } // Compiler sees: first_word<'a>(s: &'a str) -> &'a str // Rule 3: &self -> output lifetime impl Config { fn get_name(&self) -> &str { } // Compiler sees: get_name<'a>(&'a self) -> &'a str } ``` ### When Elision Doesn't Work ```rust // Error: return reference with unclear lifetime fn longest(x: &str, y: &str) -> &str { // Compiler error! if x.len() > y.len() { x } else { y } } // Solution: add explicit lifetime fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` ## Structs with References Structs holding references need lifetime annotations: ```rust struct ImportantExcerpt<'a> { part: &'a str, } fn main() { let novel = String::from("Call me Ishmael..."); let first_sentence = novel.split('.').next().unwrap(); let excerpt = ImportantExcerpt { part: first_sentence, }; } ``` ### Method Lifetimes ```rust impl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { 3 } fn announce_and_return(&self, announcement: &str) -> &str { println!("Attention: {}", announcement); self.part } } ``` ## Static Lifetime `'static` means the reference lives for the entire program: ```rust let s: &'static str = "I live forever"; fn main() { // String literals have 'static lifetime let s = "hello"; // &'static str // &'static references can outlive any data } ``` ### When to Use 'static ```rust // Error messages fn generic() { } // T: 'static sometimes needed // Global constants const GREETING: &str = "Hello"; // &'static // Box with static data fn get_static_string() -> &'static str { "a static string" } ``` ## Lifetime Subtyping One lifetime outlives another: ```rust struct Context<'a, 'b> { part: &'a str, other: &'b str, } fn main() { let s1 = String::from("long string"); let s2 = String::from("short"); { let excerpt = Context { part: s1.as_str(), other: s2.as_str(), }; } // excerpt dropped here } ``` ## Multiple Lifetimes ```rust fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str { if x.len() > y.len() { x } else { x // Return 'a anyway (could return y with &'b) } } ``` ## Lifetime in Methods ```rust struct Owner(i32); impl Owner { fn add_one<'a>(&'a mut self) { self.0 += 1; } fn print<'a>(&'a self) { println!("{}", self.0); } } fn main() { let mut owner = Owner(10); owner.add_one(); owner.print(); } ``` ## Trait Bounds with Lifetimes ```rust use std::fmt::Display; fn longest_with_anouncement<'a, T> where T: 'a, T: Display, { // ... } ``` ## Common Patterns ### Reference in Struct ```rust struct Ref<'a, T> { data: &'a T, } fn main() { let x = 5; let r = Ref { data: &x }; println!("{:?}", r.data); } ``` ### Multiple References ```rust struct MultiRef<'a, 'b, 'c, T> { first: &'a T, second: &'b T, third: &'c T, } ``` ## Summary - Lifetimes prevent dangling references - Annotations: `&'a T` means reference lives at least 'a - Elision rules handle common cases automatically - Structs holding references need lifetime parameters - `'static` means reference lives for entire program - Multiple lifetimes when needed - Methods use `impl<'a>` syntax - Lifetime bounds: `T: 'a`

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →