Crate once_cell[][src]

Overview

once_cell provides two new cell-like types, unsync::OnceCell and sync::OnceCell. OnceCell might store arbitrary non-Copy types, can be assigned to at most once and provide direct access to the stored contents. In a nutshell, API looks roughly like this:

impl OnceCell<T> {
    fn set(&self, value: T) -> Result<(), T> { ... }
    fn get(&self) -> Option<&T> { ... }
}

Note that, like with RefCell and Mutex, the set method requires only a shared reference. Because of the single assignment restriction get can return an &T instead of ReF<T> or MutexGuard<T>.

Patterns

OnceCell might be useful for a variety of patterns.

Safe Initialization of global data

use std::{env, io};
use once_cell::sync::OnceCell;

#[derive(Debug)]
pub struct Logger {
    // ...
}
static INSTANCE: OnceCell<Logger> = OnceCell::INIT;

impl Logger {
    pub fn global() -> &'static Logger {
        INSTANCE.get().expect("logger is not initialized")
    }

    fn from_cli(args: env::Args) -> Result<Logger, io::Error> {
       // ...
    }
}

fn main() {
    let logger = Logger::from_cli(env::args()).unwrap();
    INSTANCE.set(logger).unwrap();
    // use `Logger::global()` from now on
}

Lazy initialized global data

This is essentially lazy_static! macro, but without a macro.

use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::OnceCell;

fn global_data() -> &'static Mutex<HashMap<i32, String>> {
    static INSTANCE: OnceCell<Mutex<HashMap<i32, String>>> = OnceCell::INIT;
    INSTANCE.get_or_init(|| {
        let mut m = HashMap::new();
        m.insert(13, "Spica".to_string());
        m.insert(74, "Hoyten".to_string());
        Mutex::new(m)
    })
}

There are also sync::Lazy and unsync::Lazy convenience types and macros to streamline this pattern:

#[macro_use]
extern crate once_cell;

use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::Lazy;

static GLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = sync_lazy! {
    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());
}

General purpose lazy evaluation

Unlike lazy_static!, Lazy works with local variables.

use once_cell::unsync::Lazy;

fn main() {
    let ctx = vec![1, 2, 3];
    let thunk = Lazy::new(|| {
        ctx.iter().sum::<i32>()
    });
    assert_eq!(*thunk, 6);
}

If you need a lazy field in a struct, you probably should use OnceCell directly, because that will allow you to access self during initialization.

use std::{fs, io::{self, Read}, path::PathBuf};
use once_cell::unsync::OnceCell;

struct Ctx {
    config_path: PathBuf,
    config: OnceCell<String>,
}

impl Ctx {
    pub fn get_config(&self) -> Result<&str, io::Error> {
        let cfg = self.config.get_or_try_init(|| -> Result<String, io::Error> {
            let mut buf = String::new();
            fs::File::open(&self.config_path)?
                .read_to_string(&mut buf)?;
            Ok(buf)
        })?;
        Ok(cfg.as_str())
    }
}

Implementation details

Implementation is based on lazy_static and lazy_cell crates and in some sense just streamlines and unifies the APIs of those crates.

To implement a sync flavor of OnceCell, this crates uses either ::std::sync::Once or ::parking_lot::Once. This is controlled by the parking_lot feature, which is enabled by default.

When using parking_lot, the crate is compatible with rustc 1.25.0, without parking_lot a minimum of 1.29.0 is required.

This crate uses unsafe.

Modules

sync
unsync

Macros

sync_lazy

Creates a new lazy value initialized by the given closure block. This macro works in const contexts. If you need a move closure, use Lazy::new constructor function.

unsync_lazy

Creates a new lazy value initialized by the given closure block. This macro works in const contexts. If you need a move closure, use Lazy::new constructor function.