What is the difference between 'a and 'static

'a is a generic lifetime parameter for flexible reference durations, while 'static is a fixed lifetime lasting the entire program execution.

The lifetime variable versus the lifetime constant

You're writing a function that returns a string slice. Most of the time, it returns a piece of data you just read from a file. But if the file is missing, you want to fall back to a hardcoded default like "default_mode". You write the function, return both paths, and the compiler rejects you. It complains about mismatched types. You have a reference tied to the file data and a reference that lives forever. Rust sees two different durations and refuses to merge them into one return type.

This is the clash between 'a and 'static. One is a placeholder for "some duration." The other is a concrete promise that the data lives for the entire program. Understanding the difference stops you from fighting the compiler over return types and helps you design APIs that are flexible where they need to be and strict where they must be.

Lifetimes are durations, not ownership

Rust's ownership system tracks who is responsible for freeing memory. Lifetimes track how long a reference is valid. A reference is only safe if the data it points to stays alive. The compiler uses lifetimes to prove that references never dangle.

'a is a generic lifetime parameter. It's a variable. When you write a function signature with 'a, you're saying "this function works for any duration that the caller provides." The caller decides how long the data lives. The function just promises that the output won't outlive the input.

'static is not a variable. It's a specific, concrete lifetime. It means "this data lives for the entire duration of the program." It doesn't depend on a function call, a scope, or a heap allocation. It exists from the moment the binary starts until the process exits. String literals, static variables, and data embedded in the binary's read-only section all have the 'static lifetime.

Think of 'a like a library book rental. The duration is defined by the rental agreement. The book is valid only while the agreement holds. Think of 'static like text etched into a stone monument. It's there from the start, it stays until the end, and it doesn't depend on anyone checking it out.

Minimal examples

Here's how the syntax looks in practice. The first function uses 'a to tie the output to the input. The second function returns 'static because the data is embedded in the binary.

/// Returns the first word of a string slice.
/// The returned slice lives as long as the input slice.
fn first_word<'a>(s: &'a str) -> &'a str {
    let bytes = s.as_bytes();
    // Iterate to find the first space.
    for (i, &byte) in bytes.iter().enumerate() {
        if byte == b' ' {
            // Return the slice up to the space.
            // The lifetime is tied to `s` via `'a`.
            return &s[..i];
        }
    }
    // No space found, return the whole slice.
    // Still tied to the input lifetime.
    &s[..]
}

/// Returns a hardcoded message.
/// This data is stored in the binary and lives forever.
fn get_default_message() -> &'static str {
    // String literals are embedded in the binary.
    // They have the `'static` lifetime.
    "Hello, world!"
}

fn main() {
    // `first_word` works with any string data.
    let text = String::from("Hello world");
    let word = first_word(&text);
    println!("{}", word);

    // `get_default_message` returns a `'static` reference.
    let msg = get_default_message();
    println!("{}", msg);
}

The compiler infers 'a when you call first_word. If you pass a &String, the compiler substitutes 'a with the lifetime of that String. The return value inherits that lifetime. If the String drops, the return value becomes invalid. With get_default_message, there's no inference. The compiler checks that the string literal actually lives forever. It verifies the data doesn't depend on any stack frame or heap allocation that could be destroyed.

Lifetimes are about validity chains. 'a extends the chain from input to output. 'static breaks the chain entirely because the data has no dependencies.

Subtyping and coercion

A crucial detail that saves you from boilerplate is that 'static is a subtype of any lifetime 'a. If a function expects a reference that lives for 'a, and you have a reference that lives for 'static, the compiler accepts it automatically. The 'static data outlives 'a, so it's safe to treat it as 'a.

This coercion happens silently. You rarely need to write it manually.

/// Accepts a string slice with any lifetime.
fn print_message<'a>(msg: &'a str) {
    println!("{}", msg);
}

fn main() {
    // `local` has a limited lifetime.
    let local = String::from("Dynamic message");
    print_message(&local);

    // `"Static message"` has `'static` lifetime.
    // The compiler coerces it to match the `'a` parameter.
    print_message("Static message");
}

The function print_message is flexible. It accepts borrowed strings, string literals, and even &str slices from other sources. The 'static literal slides in because it satisfies the "lives at least as long as 'a" requirement. This is why you can pass string literals to almost any function that takes &str.

Trust the subtype relationship. The compiler handles the coercion. You don't need to annotate 'static in function signatures unless you want to enforce that bound on the caller.

Realistic scenario: the config loader

Back to the config loader from the opener. You want a function that returns a setting. If the setting exists in the config, return it. Otherwise, return a default.

struct Config {
    data: String,
}

/// Attempts to load a setting from config.
/// This function fails to compile because the return type
/// cannot represent two different lifetimes.
fn get_setting_broken(config: &Config) -> &str {
    if config.data.contains("setting") {
        // Returns a reference tied to `config`.
        // Lifetime is bounded by `config`.
        &config.data
    } else {
        // Returns a reference tied to the binary.
        // Lifetime is `'static`.
        "default"
        }
    }
}

The compiler rejects this with E0308 (mismatched types). The return type &str implies a single lifetime. The if branch returns a reference with a lifetime tied to config. The else branch returns a 'static reference. Rust cannot unify these into one lifetime. The return type would have to be both "tied to config" and "independent of config" at the same time, which is impossible.

The fix depends on your design. If you need to return both owned data and static data, return an owned String or use Cow. If you can guarantee the return is always 'static, change the signature to -> &'static str and adjust the logic. Don't force 'static just to make the error go away. If the data comes from a file, it's not 'static.

Don't fight the compiler here. Reach for Cow or return owned data when the lifetimes diverge.

Threads and async: the 'static requirement

The most common place you'll encounter a hard requirement for 'static is when spawning threads or tasks. Background threads run independently of the main thread. They might outlive the scope that created them. The compiler demands 'static for data captured by the thread to ensure the data doesn't vanish while the thread is still running.

use std::thread;

fn main() {
    let data = String::from("Thread payload");

    // This fails: `data` is dropped when `main` ends,
    // but the thread might still be running.
    // thread::spawn(move || {
    //     println!("{}", data);
    // });

    // Fix: Clone the data so the thread owns its copy.
    let thread_data = data.clone();
    thread::spawn(move || {
        println!("{}", thread_data);
    });
}

If you try to move data directly into the closure, the compiler rejects you. The closure requires a 'static lifetime because spawn hands it off to the OS thread pool. The thread pool doesn't know when main will return. It assumes the task might run forever. The compiler enforces 'static to prevent dangling pointers in the thread.

This is a safety guarantee. Threads are the great equalizer. They force you to confront the duration of your data. If you can't prove it lives forever, you can't send it to a thread. Clone the data, use Arc, or restructure the logic.

Pitfalls and conventions

A common trap is assuming 'static implies global mutable state. It only describes duration. You can have a 'static reference to data that is completely private to a function, as long as that data lives forever. String literals are 'static but they aren't global variables. They're just embedded constants.

Another trap is over-constraining. If you write fn process(key: &'static str), you reject valid inputs like &String or &str from a file. The compiler will reject this with E0308 because &str is not automatically 'static. Use 'a unless you have a specific reason to require the data to outlive the program's logic. Flexibility is usually better.

Memory leaks are a tool, not a bug. You can turn owned data into 'static references using Box::leak. This allocates memory on the heap and returns a 'static pointer, intentionally leaking the allocation. Use this only when you need a 'static reference to dynamic data and you accept the memory cost.

/// Converts a dynamic string into a static reference.
/// This leaks memory intentionally.
fn make_static(input: String) -> &'static str {
    // `Box::leak` turns the owned box into a raw pointer,
    // then wraps it in a `'static` reference.
    // The memory is never freed.
    Box::leak(input.into_boxed_str())
}

Convention aside: Box::leak should be near the allocation or clearly marked. The community treats leak as a signal that you considered the lifetime and chose to pay the memory cost. Don't hide it inside a helper function without documentation. Treat the leak as a contract with the reader.

Static variables are another source of 'static data. static variables are stored in the binary and live forever. By convention, static variables are uppercase, similar to const. The difference is that static variables have a memory address and can be mutable with synchronization, while const values are inlined.

Treat 'static as a promise, not a magic wand. If you can't prove the data lives forever, the compiler is right to reject you.

Decision matrix

Use 'a when your function accepts a reference and returns a reference derived from that input. The output lifetime must be tied to the input lifetime so the compiler knows the output dies when the input dies. Use 'a when you want maximum flexibility. A function taking &'a str accepts string literals, string slices, and borrowed strings from String values. Use 'static when the data is embedded in the binary, like string literals or static variables. Use 'static when you are building a data structure that stores references and you want to avoid tracking lifetimes internally. A HashMap<&'static str, V> is easier to manage than HashMap<&'a str, V> because the keys never expire. Use 'static when you are passing data to a background thread or an async runtime that might outlive the current scope. The runtime needs to guarantee the data won't vanish while the task runs.

Default to 'a. Reach for 'static only when the duration is a hard requirement of the design.

Where to go next