Movatterモバイル変換


[0]ホーム

URL:


Docs.rs

Crateonce_cell

Source
Expand description

§Overview

once_cell provides two new cell-like types,unsync::OnceCell andsync::OnceCell. AOnceCell might store arbitrary non-Copy types, canbe assigned to at most once and provides direct access to the storedcontents. The core API looksroughly like this (and there’s much moreinside, read on!):

impl<T> OnceCell<T> {const fnnew() -> OnceCell<T> { ... }fnset(&self, value: T) ->Result<(), T> { ... }fnget(&self) ->Option<&T> { ... }}

Note that, like withRefCell andMutex, theset method requiresonly a shared reference. Because of the single assignment restrictiongetcan return a&T instead ofRef<T> orMutexGuard<T>.

Thesync flavor is thread-safe (that is, implements theSync trait),while theunsync one is not.

§Recipes

OnceCell might be useful for a variety of patterns.

§Safe Initialization of Global Data

usestd::{env, io};useonce_cell::sync::OnceCell;#[derive(Debug)]pub structLogger {// ...}staticINSTANCE: OnceCell<Logger> = OnceCell::new();implLogger {pub fnglobal() ->&'staticLogger {        INSTANCE.get().expect("logger is not initialized")    }fnfrom_cli(args: env::Args) ->Result<Logger, std::io::Error> {// ...}}fnmain() {letlogger = Logger::from_cli(env::args()).unwrap();    INSTANCE.set(logger).unwrap();// use `Logger::global()` from now on}

§Lazy Initialized Global Data

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

usestd::{sync::Mutex, collections::HashMap};useonce_cell::sync::OnceCell;fnglobal_data() ->&'staticMutex<HashMap<i32, String>> {staticINSTANCE: OnceCell<Mutex<HashMap<i32, String>>> = OnceCell::new();    INSTANCE.get_or_init(|| {letmutm = HashMap::new();        m.insert(13,"Spica".to_string());        m.insert(74,"Hoyten".to_string());        Mutex::new(m)    })}

There are also thesync::Lazy andunsync::Lazy convenience types tostreamline this pattern:

usestd::{sync::Mutex, collections::HashMap};useonce_cell::sync::Lazy;staticGLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| {letmutm = HashMap::new();    m.insert(13,"Spica".to_string());    m.insert(74,"Hoyten".to_string());    Mutex::new(m)});fnmain() {println!("{:?}", GLOBAL_DATA.lock().unwrap());}

Note that the variable that holdsLazy is declared asstatic,notconst. This is important: usingconst instead compiles, but works wrong.

§General purpose lazy evaluation

Unlikelazy_static!,Lazy works with local variables.

useonce_cell::unsync::Lazy;fnmain() {letctx =vec![1,2,3];letthunk = Lazy::new(|| {        ctx.iter().sum::<i32>()    });assert_eq!(*thunk,6);}

If you need a lazy field in a struct, you probably should useOnceCelldirectly, because that will allow you to accessself duringinitialization.

usestd::{fs, path::PathBuf};useonce_cell::unsync::OnceCell;structCtx {    config_path: PathBuf,    config: OnceCell<String>,}implCtx {pub fnget_config(&self) ->Result<&str, std::io::Error> {letcfg =self.config.get_or_try_init(|| {            fs::read_to_string(&self.config_path)        })?;Ok(cfg.as_str())    }}

§Lazily Compiled Regex

This is aregex! macro which takes a string literal and returns anexpression that evaluates to a&'static Regex:

macro_rules! regex {    ($re:literal $(,)?) => {{staticRE: once_cell::sync::OnceCell<regex::Regex> = once_cell::sync::OnceCell::new();        RE.get_or_init(|| regex::Regex::new($re).unwrap())    }};}

This macro can be useful to avoid the “compile regex on every loopiteration” problem.

§Runtimeinclude_bytes!

Theinclude_bytes macro is useful to include test resources, but it slowsdown test compilation a lot. An alternative is to load the resources atruntime:

usestd::path::Path;useonce_cell::sync::OnceCell;pub structTestResource {    path:&'staticstr,    cell: OnceCell<Vec<u8>>,}implTestResource {pub const fnnew(path:&'staticstr) -> TestResource {        TestResource { path, cell: OnceCell::new() }    }pub fnbytes(&self) ->&[u8] {self.cell.get_or_init(|| {letdir = std::env::var("CARGO_MANIFEST_DIR").unwrap();letpath = Path::new(dir.as_str()).join(self.path);            std::fs::read(&path).unwrap_or_else(|_err| {panic!("failed to load test resource: {}", path.display())            })        }).as_slice()    }}staticTEST_IMAGE: TestResource = TestResource::new("test_data/lena.png");#[test]fntest_sobel_filter() {letrgb:&[u8] = TEST_IMAGE.bytes();// ...}

§lateinit

LateInit type for delayed initialization. It is reminiscent of Kotlin’slateinit keyword and allows construction of cyclic data structures:

useonce_cell::sync::OnceCell;pub structLateInit<T> { cell: OnceCell<T> }impl<T> LateInit<T> {pub fninit(&self, value: T) {assert!(self.cell.set(value).is_ok())    }}impl<T> DefaultforLateInit<T> {fndefault() ->Self{ LateInit { cell: OnceCell::default() } }}impl<T> std::ops::DerefforLateInit<T> {typeTarget = T;fnderef(&self) ->&T {self.cell.get().unwrap()    }}#[derive(Default)]structA<'a> {    b: LateInit<&'aB<'a>>,}#[derive(Default)]structB<'a> {    a: LateInit<&'aA<'a>>}fnbuild_cycle() {leta = A::default();letb = B::default();    a.b.init(&b);    b.a.init(&a);let_a =&a.b.a.b.a;}

§Comparison with std

!Sync typesAccess ModeDrawbacks
Cell<T>TrequiresT: Copy forget
RefCell<T>RefMut<T> /Ref<T>may panic at runtime
unsync::OnceCell<T>&Tassignable only once
Sync typesAccess ModeDrawbacks
AtomicTTworks only with certainCopy types
Mutex<T>MutexGuard<T>may deadlock at runtime, may block the thread
sync::OnceCell<T>&Tassignable only once, may block the thread

Technically, callingget_or_init will also cause a panic or a deadlock ifit recursively calls itself. However, because the assignment can happen onlyonce, such cases should be more rare than equivalents withRefCell andMutex.

§Minimum Supportedrustc Version

If only thestd,alloc, orrace features are enabled, MSRV will beupdated conservatively, supporting at least latest 8 versions of the compiler.When using other features, likeparking_lot, MSRV might be updated morefrequently, up to the latest stable. In both cases, increasing MSRV isnotconsidered a semver-breaking change and requires only a minor version bump.

§Implementation details

The implementation is based on thelazy_static andlazy_cell crates andstd::sync::Once. In some sense,once_cell just streamlines and unifiesthose APIs.

To implement a sync flavor ofOnceCell, this crates uses either a customre-implementation ofstd::sync::Once orparking_lot::Mutex. This iscontrolled by theparking_lot feature (disabled by default). Performanceis the same for both cases, but theparking_lot basedOnceCell<T> issmaller by up to 16 bytes.

This crate usesunsafe.

§F.A.Q.

Should I use the sync or unsync flavor?

Because Rust compiler checks thread safety for you, it’s impossible toaccidentally useunsync wheresync is required. So, useunsync insingle-threaded code andsync in multi-threaded. It’s easy to switchbetween the two if code becomes multi-threaded later.

At the moment,unsync has an additional benefit that reentrantinitialization causes a panic, which might be easier to debug than adeadlock.

Does this crate support async?

No, but you can useasync_once_cell instead.

Does this crate supportno_std?

Yes, but with caveats.OnceCell is a synchronization primitive whichsemantically relies on blocking.OnceCell guarantees that at most onef will be called to compute the value. If two threads of execution callget_or_init concurrently, one of them has to wait.

Waiting fundamentally requires OS support. Execution environment needs tounderstand who waits on whom to prevent deadlocks due to priority inversion.Youcould make code to compile by blindly using pure spinlocks, but theruntime behavior would be subtly wrong.

Given these constraints,once_cell provides the following options:

  • Therace module provides similar, but distinct synchronization primitivewhich is compatible withno_std. Withrace, thef function can becalled multiple times by different threads, but only one thread will winto install the value.
  • critical-section feature (with a-, not_) usescritical_sectionto implement blocking.

Can I bring my own mutex?

There isgeneric_once_cell toallow just that.

Should I usestd::cell::OnceCell,once_cell, orlazy_static?

If you can usestd version (your MSRV is at least 1.70, and you don’t needextra featuresonce_cell provides), usestd. Otherwise, useonce_cell.Don’t uselazy_static.

§Related crates

Modules§

race
Thread-safe, non-blocking, “first one wins” flavor ofOnceCell.
sync
Thread-safe, blocking version ofOnceCell.
unsync
Single-threaded version ofOnceCell.

[8]ページ先頭

©2009-2025 Movatter.jp