Skip to main content

OnceCell

Struct OnceCell 

Source
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>

Source

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");
Source

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));
Source

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));
Source

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);
Source

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);
Source

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);
Source

pub async fn get_or_init<F, Fut>(&self, init: F) -> &T
where F: FnOnce() -> Fut, Fut: Future<Output = 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);
Source

pub async fn get_or_try_init<F, Fut, E>(&self, init: F) -> Result<&T, E>
where F: FnOnce() -> Fut, Fut: Future<Output = 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));
Source

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));

Trait Implementations§

Source§

impl<T> Debug for OnceCell<T>
where T: Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T> Default for OnceCell<T>

Source§

fn default() -> Self

Creates an empty cell.

Source§

impl<T> From<T> for OnceCell<T>

Source§

fn from(value: T) -> Self

Creates a cell that already holds value.

Auto Trait Implementations§

§

impl<T> !Freeze for OnceCell<T>

§

impl<T> !RefUnwindSafe for OnceCell<T>

§

impl<T> Send for OnceCell<T>
where OnceLock<T>: Send,

§

impl<T> Sync for OnceCell<T>
where OnceLock<T>: Sync,

§

impl<T> Unpin for OnceCell<T>
where OnceLock<T>: Unpin,

§

impl<T> UnsafeUnpin for OnceCell<T>

§

impl<T> UnwindSafe for OnceCell<T>
where OnceLock<T>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more