Introduction to Lifetimes
In this page:
fn function_name<'a>(param1: &'a Type, param2: &'a Type) -> &'a Type {
// return one of the references
}
What a Lifetime Represents
A lifetime is the compiler's internal tracking of how long a particular reference remains valid, ensuring it never outlives the data it points to.
Example: What a Lifetime Represents
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `text`, set to `String::from("valid data")`
let text = String::from("valid data");
// Declare an immutable variable `reference`, set to `&text`
let reference = &text;
// Print "{}" to the console, substituting in `reference` (with a trailing newline)
println!("{}", reference);
}
Login to try C/C++/Java/PHP code in the editor
Most Lifetimes Are Inferred
In the vast majority of code, the compiler infers lifetime relationships automatically without you writing any lifetime syntax at all.
Example: Most Lifetimes Are Inferred
// Define the function `first_word` taking `s` and returning a string slice
fn first_word(s: &str) -> &str {
// Match on `s.find(' ')` and branch by pattern
match s.find(' ') {
Some(i) => &s[..i],
None => s,
}
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `sentence`, set to `String::from("hello world")`
let sentence = String::from("hello world");
// Print "{}" to the console, substituting in `first_word(&sentence)` (with a trailing newline)
println!("{}", first_word(&sentence));
}
Login to try C/C++/Java/PHP code in the editor
Lifetimes Prevent Dangling References
The entire purpose of lifetime tracking is to guarantee a reference can never be used after the data it points to has been freed, catching the mistake at compile time instead of causing a crash later.
Example: Lifetimes Prevent Dangling References
fn longest_of_two<'a>(x: &'a str, y: &'a str) -> &'a str {
// Check whether `x.len() > y.len() { x } else { y }`
if x.len() > y.len() { x } else { y }
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `a`, set to `String::from("short")`
let a = String::from("short");
// Declare an immutable variable `b`, set to `String::from("much longer string")`
let b = String::from("much longer string");
// Print "{}" to the console, substituting in `longest_of_two(&a, &b)` (with a trailing newline)
println!("{}", longest_of_two(&a, &b));
}
Login to try C/C++/Java/PHP code in the editor
No Runtime Cost
Lifetime checking happens entirely during compilation. Once the code compiles successfully, no lifetime information remains in the compiled binary, and there is zero runtime overhead.
Example: No Runtime Cost
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `value`, set to `100`
let value = 100;
// Declare an immutable variable `borrowed`, set to `&value`
let borrowed = &value;
// Print "Borrowed with zero runtime cost: {}" to the console, substituting in `borrowed` (with a trailing newline)
println!("Borrowed with zero runtime cost: {}", borrowed);
}
Login to try C/C++/Java/PHP code in the editor
- Assuming lifetimes control how long data lives -- they only describe relationships that already exist, they don't extend anything.
- Thinking every reference needs an explicit lifetime annotation -- most cases are inferred automatically without one.
- Believing lifetimes are a runtime feature -- they are purely a compile-time concept with zero runtime cost.
- A lifetime describes how long a reference remains valid relative to the data it points to.
- Most lifetimes are inferred automatically by the compiler and never need to be written explicitly.
- Lifetimes exist to prevent dangling references, entirely at compile time.
- Explicit lifetime annotations become necessary when the compiler cannot infer the relationship on its own.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: