In this tutorial, you will learn why lifetimes exist and how they keep your references safe.
Why Do Lifetimes Exist? #
In Rust, ownership and borrowing let us avoid memory bugs without a garbage collector.
But there’s still one tricky question: How does the compiler know that a reference is always valid?
That’s where lifetimes come in. A lifetime tells Rust how long a reference is valid.
The Core Rule #
A reference must never outlive the data it points to. If it could outlive, we’d get a dangling reference.
A Simple Borrow Example #
fn main() {
let s1 = String::from("hello");
let s2 = &s1; // borrow s1
println!("s1 = {}, s2 = {}", s1, s2);
}Code language: Rust (rust)This example works fine because both s1 and s2 are alive inside main, so no issue.
Reference Outliving Value Example #
fn main() {
let r;
{
let s = String::from("hello");
r = &s; // ❌ ERROR: s does not live long enough
}
println!("{}", r);
}Code language: Rust (rust)In this example:
sis created in the inner block.- When the block ends,
sis dropped. - But
ris still trying to reference it, which is not allowed.
Rust catches this error at compile time.
Lifetimes in Function Signatures #
If you use references in a function, Rust sometimes needs help knowing how long they should live. For example:
fn main() {
let s1 = "hi";
let s2 = "hello";
let s3 = longest(&s1, &s2);
println!("{}", s3);
}
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() { x } else { y }
}
Code language: Rust (rust)This code won’t compile due to the error in the longest function. Rust doesn’t know whether the return reference comes from x or y.
However, you can fix it by adding a lifetime parameter 'a:
fn main() {
let s1 = "hi";
let s2 = "hello";
let s3 = longest(&s1, &s2);
println!("{}", s3);
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}Code language: Rust (rust)In the new version:
'ameans: “the return value will live at least as long as bothxandy.”- The compiler is happy.
Do I Always Need Lifetimes? #
No. Most of the time, Rust figures them out automatically (called lifetime elision rules). You only need to write them when Rust can’t decide unambiguously.
Summary #
- Lifetimes tell Rust how long references are valid.
- They prevent dangling references at compile time.
- You don’t manage lifetimes manually and you just annotate them for the compiler.
- Most cases are inferred automatically, but in functions with multiple references, you may need to specify them.