18.6 The 'static Lifetime: References That Live for the Entire Program
Let’s talk about the 'static lifetime. It sounds intimidating, like some kind of eternal, unchangeable cosmic constant. And in a way, it is. A reference with a 'static lifetime is the golden child of borrow checking: it’s a reference that is guaranteed to be valid for the entire duration of the program’s execution. The borrow checker can just shrug and leave it alone because this reference is never, ever going to cause a dangling pointer. It’s already where all references aspire to be.
You’ll encounter 'static in two main flavors: the obvious one and the sneaky one.
The Obvious Kind: String Literals and Global Data
The most straightforward way to get a 'static reference is with data that’s baked directly into your program’s binary. String literals are the classic example.
let my_greeting: &'static str = "Hello, world!";
Why is this 'static? Because the text "Hello, world!" is hardcoded into the read-only data segment of your compiled executable. It exists before main starts and gets cleaned up when the entire process dies. Any reference to it is therefore valid for the program’s entire life. The same goes for other types of global data defined with static items.
static IMPORTANT_NUMBER: i32 = 42;
fn get_important_reference() -> &'static i32 {
&IMPORTANT_NUMBER
}
Trying to return a reference to a local variable would make the borrow checker furious, but here, it’s perfectly happy. We’re returning a reference to a memory location (IMPORTANT_NUMBER) that will always exist.
The Sneaky (and More Common) Kind: Owned Data
Here’s where newcomers get tripped up. You can coerce an owned String into a 'static string slice using a little sleight of hand, but you have to understand the consequence.
fn make_static_string(s: String) -> &'static str {
Box::leak(s.into_boxed_str())
}
Wait, what? Let’s break down this arcane ritual. We take a String (which owns its data on the heap), convert it into a Box<str>, and then use Box::leak. This function deliberately “leaks” the box, telling Rust to forget about ever dropping and deallocating this memory. The memory is now allocated on the heap, and a reference to it will remain valid until the program ends. You’ve effectively promoted a short-lived heap allocation to a permanent one.
This is a huge deal. You are now responsible for this memory. It will never be freed. Do this carelessly in a long-running program, and you’ve just created a memory leak. It’s a powerful tool, but it’s like using a fire extinguisher as a hammer—effective for a specific nail, but messy if you’re not careful. You’d typically only do this for things that truly need to live forever, like loading core configuration at startup.
‘static in Generic Code and Traits
Beyond references, you’ll see 'static used as a bound in generic code and traits. The 'static lifetime bound on a trait object (T: 'static) is a frequent source of confusion.
It doesn’t mean “this type must have a 'static lifetime.” It means “this type must be a type that we can own forever, meaning it contains no non-'static references.”
This bound is satisfied by:
- Owned types like
String,Vec<i32>, ori32(they contain no references at all). - Types that do contain references, but only
'staticones (like&'static str).
It is not satisfied by a String (good!) or a &'static str (good!), but it is not satisfied by a &'a str where 'a is some short lifetime. This distinction is crucial for understanding why you can spawn a thread with thread::spawn (which requires its closure to be Send + 'static) with a String but not with a regular string slice &str unless that slice is 'static. The closure must be able to live arbitrarily long on its new thread, so it can’t hold onto references to data that might vanish from the parent thread’s stack.
When to Use (and Not Use) ‘static
The rule of thumb is simple: default to not using it. The vast majority of your code should use elided lifetimes or shorter, explicit ones. Reach for 'static only when you have a compelling reason:
- For actual global constants that are read-only and known at compile time.
- When creating a singleton or long-lived cache that is initialized once at runtime and must persist forever.
- When interfacing with FFI or other systems that require pointers to memory that must not be moved or freed.
- When a library API forces your hand (like
thread::spawn), and you need to promote owned data to meet the'staticbound viaBox::leakor anArc.
It’s a precision tool, not a everyday wrench. Use it wisely, and you’ll have the borrow checker eating out of your hand. Misuse it, and you’ll be left with a program that runs but slowly hemorrhages memory. Choose your eternity wisely.