Static

35. LazyLock

Still pulling in lazy_static or once_cell just for a lazy global? std::sync::LazyLock does the same thing — zero dependencies.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
use std::sync::LazyLock;

static CONFIG: LazyLock<Vec<String>> = LazyLock::new(|| {
    // Imagine this reads from a file or env
    vec!["debug".to_string(), "verbose".to_string()]
});

fn main() {
    // CONFIG is initialized on first access
    println!("flags: {:?}", *CONFIG);
    assert_eq!(CONFIG.len(), 2);
}

LazyLock was stabilized in Rust 1.80 as the std replacement for once_cell::sync::Lazy and lazy_static!. It initializes the value exactly once on first access, is Sync by default, and works in static items without macros.

For single-threaded or non-static use, there’s also LazyCell — same idea but without the synchronization overhead:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::cell::LazyCell;

fn main() {
    let greeting = LazyCell::new(|| {
        println!("computing...");
        "Hello, Rust!".to_uppercase()
    });

    println!("before access");
    // "computing..." prints here, on first deref
    assert_eq!(*greeting, "HELLO, RUST!");
    // second access — no recomputation
    assert_eq!(*greeting, "HELLO, RUST!");
}

The output is:

1
2
before access
computing...

The closure runs lazily on first Deref, and the result is cached for all subsequent accesses. No unwrap(), no Mutex, no external crates — just clean lazy initialization from std.