Rust Tutorials: OnceCell and LazyCell 🎯

beginner
25 min

Rust Tutorials: OnceCell and LazyCell 🎯

Welcome to our deep dive into Rust's OnceCell and LazyCell! These powerful tools help manage shared state in a safe and efficient manner. Let's explore these concepts step-by-step.

What are OnceCell and LazyCell? 📝

OnceCell and LazyCell are part of the std::cell module in the Rust standard library. They are used to create cell types that hold a single value, but with special behavior.

  • OnceCell: guarantees that a value will be set exactly once.
  • LazyCell: provides a way to initialize a value on first access.

Why use OnceCell and LazyCell? 💡

These cell types help manage shared state in thread-safe and optimized ways. They can be used in various scenarios such as caching, logging, and singleton patterns.

OnceCell 📝

Creating a OnceCell

To create a OnceCell, use the Cell::new function and pass once as an argument.

rust
use std::cell::Cell; let once_cell = Cell::new(None);

Setting a Value

To set a value in a OnceCell, call the set method. This method will panic if the cell already has a value.

rust
once_cell.set(Some(42));

Getting a Value

To get a value from a OnceCell, call the get method. If the cell does not have a value, it will be initialized on first access.

rust
let value = once_cell.get(); // Will return Some(42) after the set call above

LazyCell 📝

Creating a LazyCell

To create a LazyCell, use the Cell::new function and pass lazy as an argument.

rust
use std::cell::Cell; let lazy_cell = Cell::new(|| { println!("Initializing..."); 42 });

Getting a Value

To get a value from a LazyCell, call the get method. The closure provided during creation will be executed if the cell does not have a value.

rust
let value = lazy_cell.get(); // Will print "Initializing..." and return 42 on first access

Advanced Examples 💡

Thread-safe Lazy Initialization 📝

Using LazyCell, we can create a thread-safe singleton.

rust
use std::sync::Once; use std::cell::Cell; use std::thread; let singleton = Cell::new(|| { println!("Initializing singleton..."); 42 }); let init = Once::new(); let handle = thread::spawn(move || { init.call_once(|| { println!("Initializing singleton in another thread..."); singleton.set(100); }); }); let value = singleton.get(); // Will return 42 initially handle.join(); let value = singleton.get(); // Will return 100 after the thread finishes

Quiz Time! 🎯