liveBook · Manning

Lifetime

As explained in Refactoring to Rust

A lifetime is the period during which a value remains valid between allocation and being dropped.

A lifetime is the period when a value is valid: after its memory is allocated and before the value is dropped. A local value might last for one function call; a global value might last for the program’s entire run. Rust checks these boundaries before the program runs, so an attempt to use a value outside its lifetime fails to compile rather than becoming an invalid memory access. section 2.3 section 2.6

Start with ownership. A local value is dropped when it goes out of scope. Passing an owned artwork to a function moves ownership into that function: the caller’s variable can no longer be used, even though the artwork remains valid under its new owner. A lifetime graph makes the change visible by marking when each variable can be used. section 2.3 section 2.2

A borrowed result must remain valid

Given `let numbers = vec![1, 2, 3, 4, 5];`, a helper can return its first element with `fn get_value(numbers: &Vec<i32>) -> &i32 { &numbers[0] }`. The compiler can infer that the returned reference is tied to the input reference. By contrast, `fn get_value() -> &i32 { let x = 4; &x }` cannot return a reference to its local `x`. If a caller still intends to use the borrowed element, it also cannot call `numbers.push(6)` first: that would mutate the vector while its immutable borrow is in use. section 4.4.1

$ cargo run
error[E0502]: cannot borrow `numbers` as mutable
  because it is also borrowed as immutable
 --> src/main.rs:6:3
  |
4 |   let value = get_value(&numbers);
  |                         -------- immutable borrow occurs here
5 |
6 |   numbers.push(6);
  |   ^^^^^^^^^^^^^^^ mutable borrow occurs here
7 |
8 |   println!("value: {}", value);
  |                         ----- immutable borrow later used here

The compiler diagnostic identifies the immutable borrow, the attempted mutable borrow, and the later use that keeps the first borrow relevant. section 4.4.1

Sometimes the compiler needs the relationship between references stated explicitly. Adding an unrelated `s: &str` argument to `get_value` leaves a signature with two input references and one output reference; the compiler cannot tell which input the output borrows from. A named lifetime resolves that question: `fn get_value<'a>(numbers: &'a Vec<i32>, s: &str) -> &'a i32` ties the result to `numbers`, not to `s`. The annotation describes the relationship; it does not make the borrowed value live longer. section 4.4.1

Listing 4.13 Returning a reference to an argument
fn main() {
  let numbers = vec![1, 2, 3, 4, 5];

  let value = get_value(&numbers, "Getting the number");

  println!("value: {}", value);
}

fn get_value<'a>(numbers: &'a Vec<i32>, s: &str) -> &'a i32 {  #1
  println!("{}", s);
  &numbers[0]
}

The annotated helper shows the small signature change needed when an additional reference makes lifetime inference insufficient. section 4.4.1

The `&'static str` type offers another concrete case: its annotation says the reference remains valid for the program’s entire runtime, as a string literal can. Lifetime checks also support sharing memory across foreign-function boundaries by making the responsibility for valid references and resource cleanup clearer. section 2.4 section 1.3

How Refactoring to Rust covers it

How deep it goes
Works from scope and move diagrams through compiler errors to named annotations relating input and output references.
Examples
Contrasts a borrowed vector element with a reference to a local variable, then adds an argument that requires a named lifetime.
What it assumes
Introduces ownership and borrowing as Rust basics before using them to explain lifetimes; assumes no prior Rust integration experience.
What you can do after
Diagnose/debug with lifetime graphs and compiler errors, then annotate a function when its returned reference must be tied to an input.
How it connects
Connects lifetimes to ownership, borrowing, scope, string literals, and foreign-function interfaces.

Also explained in

Compare how 8 books explain lifetime →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Refactoring to Rust. The section links point to the parts of the book it draws on.