pub struct OnceCell<T> { /* private fields */ }Expand description
A cell that is set once, by an initialiser that may await, and whose value tasks can wait for.
A cell starts empty. The first value that reaches it stays for as long as the cell lives, and
every task gets a reference to that one value. It is like std::sync::OnceLock, but for
tasks. get_or_init and get_or_try_init
take an initialiser that makes a future, which may await. wait suspends the
task until another task sets the value.
The cell needs no runtime and works under any executor. It is Send when T is Send, and
Sync when T is Send and Sync. Its futures can move between threads when T is Send
and Sync and the initialiser and the future it makes can move too.
§Initialisation
A task that finds the cell empty initialises it. Tasks that find it empty meanwhile wait for the
initialiser to end. One initialiser runs at a time, and the others take their turns in the order
an async Mutex serves its waiters (see fairness).
When the initialiser finishes, the value is set and every waiting task is woken. These are the
tasks in wait, and the tasks waiting for their turn to initialise the cell
or to set it. The latter find the value set, so they run no initialiser and
set no value. Each completes as soon as it is polled, without waiting for the tasks ahead of it
in line.
If an initialiser returns an error from get_or_try_init, panics,
or is given up by dropping the future that runs it, the cell stays empty. The next task in line
runs its own initialiser. A task that only calls wait is not an initialiser,
and keeps waiting.
§Re-entrancy
The future that an initialiser makes must not call get_or_init,
get_or_try_init or set on its own cell, or
wait for its value. It would wait for itself and never complete, as when a
std::sync::OnceLock is initialised from inside its own initialiser.
§No poisoning
A panic in an initialiser does not poison the cell. The cell stays empty and the next initialiser runs. See the module documentation.
§Example
A value is created when it is first requested, by work that awaits. Later requests get the same value without repeating the work:
use std::future;
use futures::executor::block_on;
use zruntime::lock::OnceCell;
let cell = OnceCell::new();
block_on(async {
let first = cell
.get_or_init(|| async {
// Create the value here, awaiting whatever that takes.
future::ready(String::from("made")).await
})
.await;
// The cell already has a value, so this initialiser is not run.
let second = cell.get_or_init(|| async { String::from("again") }).await;
assert_eq!(first, "made");
assert!(std::ptr::eq(first, second));
});Implementations§
Source§impl<T> OnceCell<T>
impl<T> OnceCell<T>
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Creates an empty cell.
This is a const fn, so a cell can be a static.
§Example
use futures::executor::block_on;
use zruntime::lock::OnceCell;
static NAME: OnceCell<&str> = OnceCell::new();
assert_eq!(NAME.get(), None);
assert_eq!(block_on(NAME.get_or_init(|| async { "zruntime" })), &"zruntime");Sourcepub fn get(&self) -> Option<&T>
pub fn get(&self) -> Option<&T>
The value, if the cell has one.
This never waits. It returns None if no value has been set, including while an initialiser
is running.
§Example
use futures::executor::block_on;
use zruntime::lock::OnceCell;
let cell = OnceCell::new();
assert_eq!(cell.get(), None);
block_on(cell.set(1)).expect("nobody set a new cell");
assert_eq!(cell.get(), Some(&1));Sourcepub fn get_mut(&mut self) -> Option<&mut T>
pub fn get_mut(&mut self) -> Option<&mut T>
A mutable reference to the value, if the cell has one.
No initialiser can be running while the cell is borrowed mutably, so this does not wait.
§Example
use zruntime::lock::OnceCell;
let mut cell = OnceCell::from(1);
*cell.get_mut().expect("the cell was made with a value") += 1;
assert_eq!(cell.get(), Some(&2));Sourcepub fn take(&mut self) -> Option<T>
pub fn take(&mut self) -> Option<T>
Takes the value out of the cell, leaving it empty.
No initialiser can be running while the cell is borrowed mutably, so this does not wait. The cell can be set again afterwards.
§Example
use zruntime::lock::OnceCell;
let mut cell = OnceCell::from(String::from("taken"));
assert_eq!(cell.take().as_deref(), Some("taken"));
assert_eq!(cell.take(), None);Sourcepub fn into_inner(self) -> Option<T>
pub fn into_inner(self) -> Option<T>
Consumes the cell and returns its value, if it has one.
§Example
use zruntime::lock::OnceCell;
assert_eq!(OnceCell::from(1).into_inner(), Some(1));
assert_eq!(OnceCell::<u8>::new().into_inner(), None);Sourcepub async fn wait(&self) -> &T
pub async fn wait(&self) -> &T
Waits for the cell to have a value and returns a reference to it.
This never initialises the cell. It waits for another task to initialise the cell or to
set the value, however many initialisers fail or are given up on the way.
If the cell already has a value, it completes at once.
§Cancel safety
Dropping the future before it completes gives up the wait. It affects no other task.
§Example
A thread waits for a value that this thread sets:
use std::{sync::Arc, thread};
use futures::executor::block_on;
use zruntime::lock::OnceCell;
let cell = Arc::new(OnceCell::new());
let waiter = thread::spawn({
let cell = cell.clone();
move || *block_on(cell.wait())
});
block_on(cell.set(5)).expect("nobody set a new cell");
assert_eq!(waiter.join().expect("the other thread did not panic"), 5);Sourcepub async fn get_or_init<F, Fut>(&self, init: F) -> &T
pub async fn get_or_init<F, Fut>(&self, init: F) -> &T
Returns the value, first setting it with init if the cell is empty.
If the cell has a value, this returns it at once without calling init. Otherwise this call
waits for its turn to initialise the cell, behind any initialiser that is running. If the
cell has a value by then, this returns it without calling init. If not, this calls init
and awaits the future it makes, and the output of that future becomes the value.
§Cancel safety
Dropping the future before it completes gives up the call. If it was awaiting the future of
init, that future is dropped and the cell stays empty. See the
type documentation for what this means for the other tasks, and
for what not to do inside the future of init.
§Example
use futures::executor::block_on;
use zruntime::lock::OnceCell;
let cell = OnceCell::new();
assert_eq!(block_on(cell.get_or_init(|| async { 1 })), &1);
// The cell has its value now, and this initialiser is not called.
assert_eq!(block_on(cell.get_or_init(|| async { 2 })), &1);Sourcepub async fn get_or_try_init<F, Fut, E>(&self, init: F) -> Result<&T, E>
pub async fn get_or_try_init<F, Fut, E>(&self, init: F) -> Result<&T, E>
Returns the value, first setting it with init if the cell is empty.
This works like get_or_init, except that the future that init
makes can fail.
§Errors
Returns the error of the future that init makes. The error goes to this call alone and the
cell stays empty. The next task in line runs its own initialiser instead of taking the
failure as the answer.
§Cancel safety
Dropping the future before it completes gives up the call, as for
get_or_init.
§Example
use futures::executor::block_on;
use zruntime::lock::OnceCell;
let cell = OnceCell::<u8>::new();
let failed = block_on(cell.get_or_try_init(|| async { Err("not available") }));
assert_eq!(failed, Err("not available"));
assert_eq!(cell.get(), None);
let made = block_on(cell.get_or_try_init(|| async { Ok::<_, &str>(1) }));
assert_eq!(made, Ok(&1));Sourcepub async fn set(&self, value: T) -> Result<&T, T>
pub async fn set(&self, value: T) -> Result<&T, T>
Sets the value of the cell if it has none, waiting for an initialiser that is running.
On success, returns a reference to the value that was set. A running initialiser gets its turn first, which is why this waits. If that initialiser sets a value, this call fails. If it fails or is given up, this call sets its value, unless another initialiser gets in first and sets one, which makes this call fail.
§Errors
Returns value in the Err if the cell already has a value.
§Cancel safety
Dropping the future before it completes gives up the call. value is dropped with it, and
the cell is left as it was.
§Example
use futures::executor::block_on;
use zruntime::lock::OnceCell;
let cell = OnceCell::new();
assert_eq!(block_on(cell.set(1)), Ok(&1));
// The cell has its value, which stays: this one comes back.
assert_eq!(block_on(cell.set(2)), Err(2));
assert_eq!(cell.get(), Some(&1));