Lazy Initialization
Recipe | Crates | Categories |
---|---|---|
std | ||
once_cell | ||
lazy_static |
Two key libraries:
once_cell
: newer crate with more ergonomic API. Should be preferred for all new projects.lazy_static
: older crate. API is less convenient, but crate is stable and maintained.
The core functionality of once_cell
is now included in the standard library with the remaining parts on track to be stabilised in future.
std
OnceCell⮳ is a cell which can be written to only once.
The corresponding Sync
version of OnceCell<T>
is OnceLock<T>
.
use std::cell::OnceCell; fn main() { let cell = OnceCell::new(); assert!(cell.get().is_none()); let value: &String = cell.get_or_init(|| "Hello, World!".to_string()); println!("{value}"); assert_eq!(value, "Hello, World!"); assert!(cell.get().is_some()); }
once_cell
once_cell
⮳ provides two cell-like types, unsync::OnceCell
and sync::OnceCell
. A OnceCell
might store arbitrary non-Copy types, can be assigned to at most once and provides direct access to the stored contents. The sync
flavor is thread-safe. once_cell
also has a once_cell::sync::Lazy
⮳ type, build on top of OnceCell
⮳:
use std::collections::HashMap; use std::sync::Mutex; use once_cell::sync::Lazy; // Must be static, not const static GLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| { let mut m = HashMap::new(); m.insert(13, "Spica".to_string()); m.insert(74, "Hoyten".to_string()); Mutex::new(m) }); fn main() { println!("{:?}", GLOBAL_DATA.lock().unwrap()); }
lazy_static
use std::collections::HashMap; use std::sync::Mutex; use std::sync::MutexGuard; use anyhow::anyhow; use lazy_static::lazy_static; // `lazy_static` allows you to define statically initialized values that are // computed lazily at runtime. It can be particularly useful for initializing // data that is expensive to compute or that needs to be shared across multiple // threads. lazy_static! { // - Any type within the macro needs to fulfill the Sync trait. // - If a type has a destructor, it will not run when the process exits. // - The `static ref` keywords are passed to the macro, they are not part of the language per se. // CONFIG is a lazily-initialized HashMap that holds configuration data. static ref CONFIG: HashMap<String, String> = { let mut m = HashMap::new(); m.insert("host".to_string(), "localhost".to_string()); m.insert("port".to_string(), "8080".to_string()); m }; // You can call a method or a function: static ref COUNT: usize = CONFIG.len(); // COUNTER is a lazily-initialized Mutex-protected integer counter. // The Mutex ensures that the COUNTER can be safely incremented from multiple threads, if needed. static ref COUNTER: Mutex<i32> = Mutex::new(0); } fn main() -> anyhow::Result<()> { // Accessing the CONFIG. println!( "Host: {}", CONFIG.get("host").ok_or(anyhow!("No host key"))? ); println!( "Port: {}", CONFIG.get("port").ok_or(anyhow!("No port key"))? ); // The macro generates a unique type that implements `Deref<TYPE>` and // stores it in a static with name `NAME`. Here, we use `*` to dereference // and retrieve the inner value. println!("Count: {}", *COUNT); // Working with the COUNTER: // The (mutable) shared state within the Mutex can only be accessed once the // lock is held. Our non-atomic increment is safe because we're the only // thread which can access the shared state when the lock is held. // We `unwrap()` the return value to assert that we are not expecting // threads to ever fail while holding the lock. { let mut counter: MutexGuard<'_, i32> = COUNTER.lock().unwrap(); *counter += 1; println!("Counter: {}", *counter); } // The lock is unlocked here // And again, on a different thread: std::thread::spawn(move || { let lock_result = COUNTER.try_lock(); // If the lock cannot be acquired at this time (or is poisoned), // `Err` is returned. if let Ok(mut counter) = lock_result { *counter += 1; println!("Counter: {}", *counter); } else { println!("try_lock failed"); } }) .join() .expect("thread::spawn failed"); Ok(()) }
[lazy_initialization: write, dedupe with global_static (P1)](https://github.com/john-cd/rust_howto/issues/411)