Lifetimes in Rust
In this lesson, you will learn about lifetimes in Rust. Lifetimes describe how long references are valid.
They are one of the most powerful features of Rust and help prevent dangling references and memory errors at compile time.
What Is a Lifetime?
A lifetime is the scope for which a reference is valid. It ensures that references never outlive the data they point to.
Rust uses lifetimes to guarantee memory safety without a garbage collector.
Why Lifetimes Are Needed
Consider this situation:
fn main() {
let r;
{
let x = 5;
r = &x;
}
println!("{}", r);
}
This code is invalid because x goes out of scope
before r is used.
Rust detects this issue at compile time and prevents the program from running.
Lifetime Annotation Syntax
Lifetime annotations use an apostrophe followed by a name,
such as 'a.
They do not change how long a reference lives — they only describe relationships between lifetimes.
&'a i32
&'a str
Lifetimes in Functions
When a function returns a reference, Rust must know how the input and output lifetimes relate.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
This function tells Rust that the returned reference will live as long as both input references.
Using the Function
Here is how the lifetime-annotated function works in practice:
fn main() {
let s1 = String::from("Rust");
let s2 = String::from("Programming");
let result = longest(&s1, &s2);
println!("{}", result);
}
Lifetime Elision
In many cases, Rust can infer lifetimes automatically. This is called lifetime elision.
For example, this function does not require explicit lifetime annotations:
fn first_word(s: &str) -> &str {
&s[..1]
}
Rust applies built-in rules to infer the correct lifetimes.
Lifetimes in Structs
Structs that store references must also use lifetime annotations.
struct Book<'a> {
title: &'a str,
}
This ensures that the reference inside the struct does not outlive the data it points to.
Lifetimes and Methods
Lifetime annotations are often used in method definitions.
impl<'a> Book<'a> {
fn title(&self) -> &str {
self.title
}
}
Here, Rust automatically understands the lifetime of &self.
Common Lifetime Errors
Some common lifetime-related errors include:
- Returning references to local variables
- Using references after data goes out of scope
- Mismatched input and output lifetimes
Why Lifetimes Matter
Lifetimes allow Rust to:
- Prevent dangling references
- Guarantee memory safety
- Eliminate runtime memory checks
- Enable safe concurrency
📝 Practice Exercises
Exercise 1
Write a function that returns the longer of two string slices.
Exercise 2
Create a struct that holds a reference and annotate its lifetime.
Exercise 3
Identify why returning a reference to a local variable fails.
Exercise 4
Use lifetime elision in a function.
✅ Practice Answers
Answer 1
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() > b.len() { a } else { b }
}
Answer 2
struct Item<'a> {
name: &'a str,
}
Answer 3
// Local variables are dropped at end of scope
Answer 4
fn len(s: &str) -> usize {
s.len()
}
What’s Next?
Now that you understand lifetimes, the next lesson will introduce structs, which allow you to create custom data types in Rust.