When Should I Use 'static vs a Named Lifetime?

Use static for data living the entire program duration and named lifetimes to relate the validity of multiple references.

When one owner isn't enough

You're writing a parser. You have a function that takes a chunk of text and returns a token. The token needs to hold a slice of the original text so you don't copy data unnecessarily. You define a struct Token { content: &str }. The compiler rejects you. You add a lifetime 'a to the struct and the function. It compiles.

Now you want to store these tokens in a global cache so other parts of the program can look them up. You try to put the Token into a static variable. The compiler screams again. You see 'static mentioned in the error. You try changing the lifetime to 'static. It works for string literals, but fails for data loaded from a file at runtime. You're stuck juggling symbols that look like variables but act like rules.

The confusion comes from treating lifetimes as durations. They aren't. 'static is a specific duration: the entire program. Named lifetimes like 'a are not durations. They are relationships. They tie the life of one reference to the life of another. Understanding this distinction unlocks the borrow checker.

Concept in plain words

Think of 'static as a deed to land that can never be sold back. The data exists from the moment the binary starts until the process exits. String literals are the classic example. When you write "Hello", the compiler bakes that string into the read-only data section of the executable. It lives forever. A reference to it can safely have the 'static lifetime.

Named lifetimes are different. They are like lease agreements. A lease doesn't specify "30 days." It specifies that the tenant can stay as long as the lease is valid. If you have two leases for the same apartment, they must have the same expiration date. A named lifetime 'a is that expiration date. It doesn't tell the compiler how long 'a is. It tells the compiler that multiple references share the same constraint. If the data dies, the lease ends, and all references tied to 'a become invalid.

'static is a concrete lifetime bound to the binary. Named lifetimes are variables that relate references to each other.

Minimal example

Here is the simplest case for each.

/// Returns a reference to a string baked into the binary.
fn get_greeting() -> &'static str {
    // String literals are stored in the executable's data section.
    // They exist for the entire duration of the program.
    // The compiler knows this and assigns the 'static lifetime automatically.
    "Hello, world!"
}

/// Returns the longer of two string slices.
/// The output lifetime is tied to the input lifetimes.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    // The function returns either x or y.
    // The returned reference must be valid as long as the input is valid.
    // The lifetime 'a enforces this relationship.
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

In get_greeting, the return type is &'static str. The compiler verifies that the string literal lives forever. In longest, the lifetime 'a appears on the inputs and the output. This tells the compiler: "The output reference lives for some duration 'a, and the inputs also live for that same duration 'a." The compiler doesn't care how long 'a is. It cares that the output doesn't outlive the inputs.

Lifetimes are compile-time proofs. The compiler checks the constraints, then erases them. The generated machine code is identical whether you use 'static or 'a.

Walkthrough: coercion and inference

Rust has a powerful rule that often surprises beginners: 'static is the longest possible lifetime. It can be coerced to any shorter lifetime. This means a &'static str can be used anywhere a &'a str is expected.

This coercion is why &str is so flexible. When you write a function signature like fn print(s: &str), the compiler infers a lifetime 'a. You can pass a string literal to this function. The literal is 'static, which is longer than 'a, so the coercion succeeds. You don't need to write &'static str in the signature. Writing &str accepts both static data and borrowed data.

Convention aside: The community standard is to use &str in function parameters unless you have a specific reason to require 'static. Writing &'static str on a parameter restricts the caller unnecessarily. It forces the caller to provide data that lives forever. Most data doesn't. Use &str to accept any reference, including static ones. Reserve 'static for return types or struct fields where you explicitly need to enforce that the data lives for the whole program.

Realistic example

Consider a configuration registry. The registry holds references to configuration keys. The keys might come from a file, or they might be hardcoded defaults.

/// A registry that stores references to configuration keys.
/// The named lifetime 'a ties the registry to the data it borrows.
struct Registry<'a> {
    keys: Vec<&'a str>,
}

impl<'a> Registry<'a> {
    /// Creates an empty registry.
    fn new() -> Self {
        Registry { keys: Vec::new() }
    }

    /// Adds a key to the registry.
    /// The key must live at least as long as the registry.
    fn add(&mut self, key: &'a str) {
        self.keys.push(key);
    }
}

fn main() {
    let mut reg = Registry::new();

    // String literals are 'static.
    // They coerce to the lifetime 'a of the Registry.
    reg.add("database_url");
    reg.add("api_key");

    // This works because the literals live forever.
    // The Registry borrows from data that outlives it.
    println!("Keys: {:?}", reg.keys);
}

The Registry struct has a lifetime 'a. This means the Registry cannot outlive the data it references. When you add a literal, the literal's 'static lifetime coerces to 'a. The compiler sees that 'static is longer than 'a, so the borrow is safe. If you tried to add a reference to a local variable that goes out of scope, the compiler would reject it. The named lifetime enforces the boundary.

A struct with a lifetime is a cage. The struct cannot escape the scope of the data it borrows.

Pitfalls and compiler errors

Missing lifetime specifier

If you define a struct with a reference but forget the lifetime, the compiler stops you.

struct BadConfig {
    path: &str,
}

The compiler rejects this with E0106 (missing lifetime specifier). The compiler needs to know how long the reference in path lives. It cannot infer a lifetime for a struct definition. You must add a named lifetime.

struct Config<'a> {
    path: &'a str,
}

Over-constraining with 'static

A common mistake is using 'static to silence the compiler when it's not needed.

fn print_static(s: &'static str) {
    println!("{}", s);
}

fn main() {
    let message = String::from("Hello");
    // Error: borrowed value does not live long enough
    print_static(&message);
}

The compiler rejects the call with E0597 (borrowed value does not live long enough). The String is dropped at the end of main. It doesn't live forever. The function demands 'static, so the call fails. The fix isn't to change the data. The fix is to relax the function signature to fn print(s: &str).

Confusing static and 'static

static is a keyword for declaring global variables. 'static is a lifetime. They are related but distinct.

static GLOBAL: &str = "I am global";

Here, GLOBAL is a static variable. Its type is &'static str. The variable itself lives forever, so the reference must be 'static. Beginners often try to use 'static as a type or misuse static in type annotations. Remember: static declares a variable. 'static annotates a lifetime.

Don't use 'static to silence the compiler. It breaks the API. Use named lifetimes to express the actual dependency.

Decision: when to use 'static vs named lifetimes

Use 'static for string literals and global constants that are baked into the binary. Use 'static when a reference must survive across threads or be stored in a global static variable, ensuring the data never gets freed. Use named lifetimes like 'a for functions that return a reference based on an input argument, tying the output's validity to the input. Use named lifetimes for structs that contain references, forcing the struct to be dropped before the borrowed data. Use named lifetimes when you need to express that two references must overlap in time, such as a cursor pointing into a buffer. Reach for owned types like String or Vec<T> when you need to return data from a function without tying it to an input lifetime.

Named lifetimes are the default tool. 'static is a special case. If you're reaching for 'static on a function parameter, you're probably making your code too rigid.

Where to go next