LazyLock<T>
std::sync::LazyLock<T, F = fn() -> T> (stabilized in Rust 1.80) is the thread-safe counterpart to LazyCell<T>. It pairs a const-constructible synchronization state with an initialization function F that executes automatically on the first dereference (*lazy) from any thread, replacing the historical lazy_static! macro in standard Rust.
How LazyLock<T> Works in static Declarations
Because static items in Rust must be initialized with compile-time const expressions, heap-allocated types such as HashMap<K, V>, compiled regular expressions, or environment-derived structs cannot be constructed directly in a static initializer. LazyLock::new(|| ...) is a const fn that stores the initialization function pointer at compile time and defers executing it until the static item is first dereferenced at runtime:
static REGISTRY: LazyLock<HashMap<...>> = LazyLock::new(|| { ... });
Compile Time:
Stores function pointer in static data segment (0 heap allocations at startup)
First Runtime Deref (`REGISTRY.get("key")`):
Thread acquires internal `Once` lock -> runs closure -> stores `HashMap` -> releases lock
All Subsequent Derefs Across Any Thread:
Fast atomic load -> returns `&HashMap` via `Deref`
use std::collections::HashMap;
use std::sync::LazyLock;
use std::thread;
static MIME_TYPES: LazyLock<HashMap<&'static str, &'static str>> = LazyLock::new(|| {
println!(" [initializing MIME_TYPES static table once]");
HashMap::from([
("rs", "text/x-rust"),
("json", "application/json"),
("wasm", "application/wasm"),
])
});
fn main() {
println!("main started (MIME_TYPES not yet initialized)");
thread::scope(|s| {
s.spawn(|| {
println!("thread 1 lookup .rs: {:?}", MIME_TYPES.get("rs"));
});
s.spawn(|| {
println!("thread 2 lookup .wasm: {:?}", MIME_TYPES.get("wasm"));
});
});
}
main started (MIME_TYPES not yet initialized)
[initializing MIME_TYPES static table once]
thread 1 lookup .rs: Some("text/x-rust")
thread 2 lookup .wasm: Some("application/wasm")
Comparing OnceCell, LazyCell, OnceLock, and LazyLock
| Type | Thread Safe (Sync)? |
Initializer Provided At | Implements Deref<Target = T>? |
Typical Use Case |
|---|---|---|---|---|
OnceCell<T> |
No (!Sync) |
Access time (.get_or_init / .set) |
No | Single-threaded lazy struct fields that depend on &self |
LazyCell<T, F> |
No (!Sync) |
Construction time (LazyCell::new(f)) |
Yes | Single-threaded local lazy values with fixed initializer |
OnceLock<T> |
Yes (Sync if T: Send + Sync) |
Access time (.get_or_init / .set) |
No | Global static or shared struct field initialized with runtime inputs |
LazyLock<T, F> |
Yes (Sync if T: Send + Sync, F: Send) |
Construction time (LazyLock::new(f)) |
Yes | Self-contained static globals (lookup tables, regexes, thread pools) |
Explicit Forcing with LazyLock::force
When you want to trigger initialization eagerly (for example, during application warm-up before serving traffic) without calling a method on T, LazyLock::force(&STATIC_ITEM) initializes the value and returns &T.