Lifetimes
Lifetime
Lifetimes are a mechanism that the Rust compiler uses to ensure that references are valid for as long as they are used (i.e. ensure all borrows are valid.) Lifetimes help prevent dangling references, which occur when a reference points to memory that has been deallocated or otherwise invalidated.
A lifetime represents the scope for which a reference is valid. Lifetime names are always prefixed with an apostrophe (e.g., 'a
, 'b
, 'static
). By convention, short, lowercase names are usually used. 'static
is a special lifetime that means the reference is valid for the entire duration of the program.
Lifetimes can be added to function or method signatures, struct definitions, impl
blocks, and references. When explicitly added to references, the lifetime annotation is inserted after &
and before the mut
keywords or the type:
&'a i32
: a shared reference with an explicit lifetime'a
.&i32
: a shared reference with an implicit lifetime (very common).&'a mut i32
: a mutable reference with an explicit lifetime'a
.&mut i32
: a mutable reference with an implicit lifetime (very common).
Lifetimes are annotations, not types: Lifetimes don't change the underlying type of a variable. A &i32
is still a reference to an i32
, regardless of its lifetime annotation. The lifetime annotation just provides extra information to the compiler about how long that reference is valid.
The compiler infers most lifetimes, but explicit lifetime annotations are sometimes necessary: When the compiler can't figure out the relationships between the lifetimes of different references (especially in function signatures or struct definitions), you need to provide explicit lifetime annotations.
Lifetime Annotations in Functions
The generic lifetime 'a
will get the concrete lifetime that is equal to the smaller of the lifetimes of x
and y
:
/// This function takes two string slices, `x` and `y`, both with the same /// lifetime `'a`. It returns a string slice that is the longer of the two, also /// with the lifetime `'a`. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let (x, y) = ("short", "looooooong"); println!("{}", longest(x, y)); }
Lifetime Annotations in Struct Definitions and Methods
Lifetime annotations are used to specify the relationships between the lifetimes of different references.
struct Foo<'a> {
x: &'a i32,
}
impl<'a> Foo<'a> {
fn x(&self) -> &'a i32 { self.x }
}
/// A struct that holds a reference to a string slice. /// /// The lifetime parameter `'a` specifies that the `ImportantExcerpt` /// cannot outlive the string slice it references. struct ImportantExcerpt<'a> { part: &'a str, } impl ImportantExcerpt<'_> { fn level(&self) -> i32 { 3 } } fn main() { let ie = ImportantExcerpt { part: "a part" }; println!("{}", ie.level()); }
Static Lifetime
/// `'static` indicates that the data pointed to by the reference lives /// for the _remaining_ lifetime of the running program. It can still /// be coerced to a shorter lifetime. fn my_string() -> &'static str { // This string literal is stored directly in the binary, and therefore // has a `'static` lifetime. let s: &'static str = "I have a static lifetime."; s } fn main() { println!("{}", my_string()); }
Related Topics
- COW.
- Memory Management.
- Ownership & Borrowing.
- Rust Patterns.
- Typecasts.