Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

Rust Lifetimes

Lifetimes have a reputation for being the scariest part of Rust. The idea behind them is actually simple, and you have been relying on it already. Let us take the fear out of it.

The one rule

A reference must never outlive the value it points to. If you write down a friend's address, that note is only useful while the house exists. If the house is demolished, the note points at nothing. In other languages, using such a "dangling" reference reads random memory. Rust simply does not allow it, and the lifetime of a reference is how long the compiler knows it stays valid.

fn main() {
    let note;
    {
        let house = String::from("12 Lake Road");
        note = &house;
    }
    println!("{}", note);
}
The compiler says
error[E0597]: `house` does not live long enough

This program does not compile, on purpose. house is dropped at the inner closing bracket, but note is used after that. The compiler points at both places. Moving the println! inside the inner block, or moving house outside it, fixes the problem.

Most of the time, lifetimes are invisible

In almost every function you write, Rust works the lifetimes out by itself, using a few simple rules called elision. For example, when a function takes one reference and returns one, Rust assumes the result comes from the input. That is why fn first_word(text: &str) -> &str on the slices page needed nothing special.

When you need to write them

When a function takes two references and returns one, Rust cannot guess which input the result is tied to. You tell it with a lifetime name, written with an apostrophe, usually 'a:

fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() >= y.len() { x } else { y }
}

fn main() {
    let first = String::from("elephant");
    let second = String::from("cat");
    let winner = longer(&first, &second);
    println!("The longer word is {}", winner);
}
Output
The longer word is elephant

Read <'a> as "for some lifetime, call it a". The signature then says: both inputs live at least as long as 'a, and so does the result. In plain words, "the answer is valid as long as both inputs are". Lifetime annotations never change how long anything lives; they only describe the relationship so the compiler can check it.

Structs that hold references

A struct that stores a reference needs a lifetime too, to promise that the struct will not outlive the text it points into:

struct Highlight<'a> {
    text: &'a str,
}

impl<'a> Highlight<'a> {
    fn shout(&self) -> String {
        format!("** {} **", self.text.to_uppercase())
    }
}

fn main() {
    let article = String::from("Rust is fast. It is also safe.");
    let first_sentence = article.split('.').next().unwrap();
    let h = Highlight { text: first_sentence };
    println!("{}", h.shout());
}
Output
** RUST IS FAST **

The special 'static

'static means "lives for the whole program". Text written in quotes in your code is stored inside the program itself, so its type is &'static str. You saw it on the enums page, and it is common in function results that return fixed text.

When in doubt, a struct can own its data with String instead of borrowing with &str. That avoids lifetimes completely, at the cost of a copy, and it is a perfectly good choice while you are learning.

Practice exercises

  1. Fix the first example in two different ways and run both versions.
  2. Write fn first_of<'a>(a: &'a str, b: &'a str) -> &'a str that returns whichever comes first alphabetically.
  3. Change Highlight to own a String instead, and remove the lifetimes.