Struct state::LocalInitCell

source ·
pub struct LocalInitCell<T> { /* private fields */ }
Expand description

A thread-local init-once-per-thread cell for thread-local values.

A LocalInitCell instance allows global access to a n per-thread values, all of which are initialized in the same manner when the value is first retrieved from a thread. The initialization function for values in LocalInitCell is specified via the set method. The initialization function must be set before a value is attempted to be retrieved via the get method. The try_get can be used to determine whether the LocalInitCell has been initialized before attempting to retrieve a value.

For safety reasons, values stored in LocalInitCell must be Send + 'static.

Comparison with InitCell

When the use-case allows, there are two primary advantages to using a LocalInitCell instance over a InitCell instance:

  • Values stored in LocalInitCell do not need to implement Sync.
  • There is no synchronization overhead when setting a value.

The primary disadvantages are:

  • Values are recomputed once per thread on get() where InitCell never recomputes values.
  • Values need to be 'static where InitCell imposes no such restriction.

Values LocalInitCell are not the same across different threads. Any modifications made to the stored value in one thread are not visible in another. Furthermore, because Rust reuses thread IDs, a new thread is not guaranteed to receive a newly initialized value on its first call to get.

Usage

This type is only available when the "tls" feature is enabled. To enable the feature, include the state dependency in your Cargo.toml as follows:

[dependencies]
state = { version = "0.6.0", features = ["tls"] }

Example

The following example uses LocalInitCell to store a per-thread count:

static COUNT: LocalInitCell<Cell<usize>> = LocalInitCell::new();

fn check_count() {
    let count = COUNT.get();

    // initialize the state, in case we reuse thread IDs
    count.set(0);

    // increment it, non-atomically
    count.set(count.get() + 1);

    // The count should always be 1 since the state is thread-local.
    assert_eq!(count.get(), 1);
}

fn main() {
    // setup the initializer for thread-local state
    COUNT.set(|| Cell::new(0));

    // spin up many threads that call `check_count`.
    let mut threads = vec![];
    for i in 0..10 {
        threads.push(thread::spawn(|| check_count()));
    }

    // Wait for all of the thread to finish.
    for thread in threads {
        thread.join().expect("correct count");
    }
}

Implementations§

source§

impl<T> LocalInitCell<T>

source

pub const fn new() -> LocalInitCell<T>

Create a new, uninitialized cell.

Example
use state::LocalInitCell;

static MY_GLOBAL: LocalInitCell<String> = LocalInitCell::new();
source§

impl<T: Send + 'static> LocalInitCell<T>

source

pub fn set<F>(&self, state_init: F) -> boolwhere F: Send + Sync + 'static + Fn() -> T,

Sets the initialization function for this local cell to state_init if it has not already been set before. The function will be used to initialize values on the first access from a thread with a new thread ID.

If a value has previously been set, self is unchanged and false is returned. Otherwise true is returned.

Example
static MY_GLOBAL: LocalInitCell<&'static str> = LocalInitCell::new();

assert_eq!(MY_GLOBAL.set(|| "Hello, world!"), true);
assert_eq!(MY_GLOBAL.set(|| "Goodbye, world!"), false);
source

pub fn try_get(&self) -> Option<&T>

Attempts to borrow the value in this cell. If this is the first time a thread with the current thread ID has called get or try_get for self, the value will be initialized using the initialization function.

Returns Some if the state has previously been set. Otherwise returns None.

Example
static MY_GLOBAL: LocalInitCell<&'static str> = LocalInitCell::new();

assert_eq!(MY_GLOBAL.try_get(), None);

MY_GLOBAL.set(|| "Hello, world!");

assert_eq!(MY_GLOBAL.try_get(), Some(&"Hello, world!"));
source

pub fn get(&self) -> &T

If this is the first time a thread with the current thread ID has called get or try_get for self, the value will be initialized using the initialization function.

Panics

Panics if an initialization function has not previously been set. Use try_get for a non-panicking version.

Example
static MY_GLOBAL: LocalInitCell<&'static str> = LocalInitCell::new();

MY_GLOBAL.set(|| "Hello, world!");
assert_eq!(*MY_GLOBAL.get(), "Hello, world!");

Trait Implementations§

source§

impl<T: Debug + Send + 'static> Debug for LocalInitCell<T>

source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<T> !RefUnwindSafe for LocalInitCell<T>

§

impl<T> Send for LocalInitCell<T>

§

impl<T> Sync for LocalInitCell<T>where T: Send,

§

impl<T> Unpin for LocalInitCell<T>

§

impl<T> !UnwindSafe for LocalInitCell<T>

Blanket Implementations§

source§

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

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

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

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere 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, U> Into<U> for Twhere 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 Twhere U: Into<T>,

§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

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

§

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.