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:
rust,ignore
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>
.
OnceCell
might be useful for a variety of patterns.
```rust use std::{env, io};
use once_cell::sync::OnceCell;
pub struct Logger {
// ...
}
static INSTANCE: OnceCell
impl Logger { pub fn global() -> &'static Logger { INSTANCE.get().expect("logger is not initialized") }
fn from_cli(args: env::Args) -> Result<Logger, std::io::Error> {
// ...
}
}
fn main() {
let logger = Logger::from_cli(env::args()).unwrap();
INSTANCE.set(logger).unwrap();
// use Logger::global()
from now on
}
```
This is essentially lazy_static!
macro, but without a macro.
```rust use std::{sync::Mutex, collections::HashMap};
use once_cell::sync::OnceCell;
fn globaldata() -> &'static Mutex
There are also sync::Lazy
and unsync::Lazy
convenience types to streamline this pattern:
```rust use std::{sync::Mutex, collections::HashMap}; use once_cell::sync::Lazy;
static GLOBALDATA: Lazy
fn main() { println!("{:?}", GLOBAL_DATA.lock().unwrap()); } ```
Unlike lazy_static!
, Lazy
works with local variables.
```rust use once_cell::unsync::Lazy;
fn main() {
let ctx = vec![1, 2, 3];
let thunk = Lazy::new(|| {
ctx.iter().sum::
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.
```rust use std::{fs, path::PathBuf};
use once_cell::unsync::OnceCell;
struct Ctx {
config_path: PathBuf,
config: OnceCell
impl Ctx { pub fn getconfig(&self) -> Result<&str, std::io::Error> { let cfg = self.config.getortryinit(|| { fs::readtostring(&self.configpath) })?; Ok(cfg.asstr()) } } ```
|!Sync
types | Access Mode | Drawbacks |
|----------------------|------------------------|-----------------------------------------------|
|Cell<T>
| T
| works only with Copy
types |
|RefCel<T>
| RefMut<T>
/ Ref<T>
| may panic at runtime |
|unsync::OnceCell<T>
| &T
| assignable only once |
|Sync
types | Access Mode | Drawbacks |
|----------------------|------------------------|-----------------------------------------------|
|AtomicT
| T
| works only with certain Copy
types |
|Mutex<T>
| MutexGuard<T>
| may deadlock at runtime, may block the thread |
|sync::OnceCell<T>
| &T
| assignable only once, may block the thread |
Technically, calling get_or_init
will also cause a panic or a deadlock if it recursively calls
itself. However, because the assignment can happen only once, such cases should be more rare than
equivalents with RefCell
and Mutex
.
rustc
VersionThis crate's minimum supported rustc
version is 1.31.1
.
If optional features are not enabled (default-features = false
in Cargo.toml
),
MSRV will be updated conservatively. When using specific features or default features, MSRV might be updated
more frequently, up to the latest stable. In both cases, increasing MSRV is not considered a semver-breaking
change.
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::Mutex
. This is controlled by the parking_lot
feature, which is enabled by default.
This crate uses unsafe.