Skip to main content
Rust Programming
CHAPTER 18 Beginner

Lifetimes in Rust

Updated: May 18, 2026
5 min read

# CHAPTER 18

Lifetimes in Rust

1. Chapter Introduction

Welcome to the most feared concept in Rust: Lifetimes. When you use References (&), the Borrow Checker guarantees that the reference will never outlive the data it points to (preventing Dangling Pointers). Usually, the compiler figures this out automatically. However, when functions take multiple references or structs store references, the compiler gets confused. It will demand that *you* explicitly label how long those references live.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand what a Lifetime is.
  • Use Lifetime Annotations (e.g., 'a).
  • Define Lifetimes in function signatures.
  • Store references inside Structs safely.
  • Understand the 'static lifetime.

3. The Dangling Pointer Problem

To understand lifetimes, look at this broken code:
rust
123456789
fn main() {
    let r;                // r is declared
    {
        let x = 5;        // x is created
        r = &x;           // r borrows x
    }                     // x goes out of scope and is DROPPED!
    
    // println!("r: {}", r); // ERROR! r is pointing to deleted memory!
}

The compiler blocks this because the *lifetime* of x is shorter than the *lifetime* of r.

4. Lifetime Annotations in Functions

Suppose we write a function that takes two string slices and returns the longest one.
rust
1234
// ERROR: missing lifetime specifier
// fn longest(x: &str, y: &str) -> &str {
//    if x.len() > y.len() { x } else { y }
// }

Why does this fail? The compiler doesn't know if the returned reference will point to x or y. If y gets deleted early, the returned reference might be dangling! We must annotate the relationships.

Lifetime Annotations ('a) We use a tick ' followed by a lowercase letter (conventionally 'a).

rust
123456789
// We define a generic lifetime <'a>
// We tell the compiler: "x, y, and the return value all share the same lifetime 'a"
fn longest<&#039;a>(x: &&#039;a str, y: &&#039;a str) -> &&#039;a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

*Note: Lifetime annotations do NOT change how long data actually lives. They simply explain the relationship between parameters to the compiler so it can do its safety checks.*

5. Lifetimes in Structs

Earlier, we were told to only put owned String types in Structs, not &str references. If you put a reference in a struct, you MUST annotate it. The struct cannot outlive the reference it holds!
rust
1234567891011121314
// The struct holds a lifetime 'a
struct Highlight<&#039;a> {
    part: &&#039;a str, // The reference must live at least as long as 'a
}

fn main() {
    let text = String::from("This is a long sentence.");
    let first_word = text.split(&#039; &#039;).next().unwrap();
    
    // Highlight borrows a slice of 'text'
    let h = Highlight { part: first_word };
    
    println!("Highlighted: {}", h.part);
} // Both text and h go out of scope here safely.

6. Lifetime Elision (Why you don't always need them)

If lifetimes are required, why haven't we written 'a until Chapter 18? Because the Rust compiler has built-in rules (Lifetime Elision rules). If your function only takes ONE reference parameter and returns ONE reference, the compiler assumes they share the same lifetime automatically!
rust
12345
// You write this:
fn get_word(s: &str) -> &str { ... }

// The compiler secretly expands it to this:
fn get_word<&#039;a>(s: &&#039;a str) -> &&#039;a str { ... }

You only need to write annotations when the compiler encounters ambiguity (like taking *two* references).

7. The 'static Lifetime

There is one special lifetime: 'static. It means the reference lives for the *entire duration of the program*. All string literals have a 'static lifetime because the text is hardcoded directly into the binary file.
rust
1
let s: &&#039;static str = "I am compiled directly into the binary.";

8. Common Mistakes

  • Trying to "Extend" Lifetimes: Annotations do not magically make a variable live longer. If you create a String inside a function and try to return a reference to it annotated with 'a, the compiler will still fail. The variable dies at the end of the function regardless of annotations.
  • Overcomplicating: Beginners often throw 'a and 'b everywhere when the compiler complains. 90% of the time, an architecture change (like returning an owned String instead of a reference) is the better solution.

9. Best Practices

  • Return Owned Types: If you are struggling with lifetimes in a complex function, just return an owned String or Vec using .clone(). Yes, it costs a little performance, but it saves hours of developer frustration. Optimize for performance later.

10. Exercises

  1. 1. Write a function shortest<'a>(s1: &'a str, s2: &'a str) -> &'a str.
  1. 2. Return the shorter of the two strings.
  1. 3. Call it in main and print the result.

11. MCQs with Answers

Question 1

What is the primary purpose of Lifetimes in Rust?

Question 2

What is the syntax for a Lifetime annotation?

Q3. Do Lifetime annotations actually change how long a variable lives in memory? a) Yes b) No, they simply describe the relationship between parameters to the compiler Answer: b) No, they simply describe the relationship.
Question 4

Why does fn longest(x: &str, y: &str) -> &str fail to compile?

Question 5

If you store a reference inside a Struct, what must you do?

Q6. Why haven't we needed to write lifetimes for single-parameter functions? a) Because the compiler uses "Lifetime Elision" rules to automatically guess the lifetimes in simple, unambiguous cases b) It was a bug Answer: a) Because of Lifetime Elision rules.
Question 7

What special lifetime denotes that a reference lives for the entire duration of the program?

Question 8

What type of data inherently has a 'static lifetime?

Q9. Can adding a lifetime annotation save a local variable from being dropped at the end of its function? a) Yes b) No, annotations do not alter actual scope rules Answer: b) No.
Question 10

If lifetimes become incredibly tangled and confusing, what is the easiest fallback strategy for a beginner?

12. Interview Questions

  • Q: Explain what a Lifetime Annotation is and why the compiler needs it for functions with multiple reference parameters.
  • Q: What is the 'static lifetime, and where is it most commonly seen naturally?

13. Summary

Lifetimes are the final piece of Rust's memory safety puzzle. While they seem intimidating, they are just explicit labels that help the compiler prove your code won't generate dangling pointers. Once you understand that they don't *change* memory rules, but merely *describe* them, the fear of the Borrow Checker disappears.

14. Next Chapter Recommendation

Rust guarantees safety, but sometimes its rules are too strict. What if we actually *need* multiple owners for a piece of data? In Chapter 19: Smart Pointers in Rust, we will explore advanced data structures like Box, Rc, and RefCell to bypass strict ownership rules safely.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·