🎓 A1Learner
🔍
0 of 149 lessons completed

Rust 'static Lifetimes

'static is the longest lifetime in Rust. As a bound, T: 'static means the type holds no reference that can dangle.

Text written in code lives for the whole program

A string literal is stored inside the program itself. So a reference to it may carry the longest lifetime of all — 'static, the lifetime that lasts the whole program:

let hello: &'static str = "hello";

Threads are the everyday 'static test

thread::spawn demands work that is 'static. The closure must hold no borrow of anything that will die. Its return value must hold none either. Moving owned data in is the standard answer:

use std::thread;
let name = String::from("ferris");
let handle = thread::spawn(move || {
    println!("thread says hello to {}", name);
    name.len()
});
println!("name had {} letters", handle.join().unwrap());

Borrowing into a thread is refused

Leave out the move and the closure borrows name instead of owning it. The thread might still be running after the frame that owns name is gone. So the borrow fails the 'static demand on the spot:

let name = String::from("ferris");
let handle = thread::spawn(|| {
    println!("thread says hello to {}", name); // error E0373: closure may outlive the current function, but it borrows `name`
});

Owned values pass the bound

A String holds no references at all. So it passes T: 'static. That holds even though it is created right here. It will be dropped right here too.

fn keep<T: 'static>(value: T) -> T { value }
let owned = keep(String::from("owned"));

Make a &'static at run time: Box::leak

Box::leak hands heap memory over for good. It is deliberately never freed. That is exactly why the result may promise 'static:

let leaked: &'static str = Box::leak(Box::new(String::from("leaked")));
println!("{leaked}");

Example

use std::fmt::Debug;
fn print_it(input: impl Debug + 'static) {
    println!("static-bound value = {:?}", input);
}
fn leaked_text() -> &'static str {
    Box::leak(Box::new(String::from("leaked text")))
}
fn main() {
    let literal: &'static str = "hello world";
    println!("literal = {}", literal);
    print_it(5);
    print_it(String::from("owned text"));
    println!("leaked = {}", leaked_text());
}

Key points

  • A reference to a local value does not pass.
  • Passing &i to a T: 'static parameter fails with error E0597.
  • The &i dies with its scope.
  • Tell the two 'static apart in code.
  • &'static T is one reference that never dangles.
  • T: 'static is a whole type.
  • Its insides hold no reference that can dangle.
  • A String passes the second.
  • It is not the first.
  • static mut, met in the Advanced section, is a different feature that happens to share a word.

In this example, the literal "hello world" is a &'static str: it lives in the program itself. print_it demands Debug + 'static. Both calls pass. 5 is owned. The String owned text is owned too. Neither holds a reference, so the bound is met. Both print. Passing &local instead would fail with E0597. Last, leaked_text uses Box::leak to turn a heap String into a &'static str. The memory is never freed. That is exactly why the promise holds. It prints leaked = leaked text.

Note

'static does not mean global, and it does not mean mutable.

It is only a promise about how long a reference stays valid.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Which value passes a T: 'static bound?