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 implementSync
. - There is no synchronization overhead when setting a value.
The primary disadvantages are:
- Values are recomputed once per thread on
get()
whereInitCell
never recomputes values. - Values need to be
'static
whereInitCell
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>
impl<T> LocalInitCell<T>
sourcepub const fn new() -> LocalInitCell<T>
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>
impl<T: Send + 'static> LocalInitCell<T>
sourcepub fn set<F>(&self, state_init: F) -> boolwhere
F: Send + Sync + 'static + Fn() -> T,
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);
sourcepub fn try_get(&self) -> Option<&T>
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!"));
sourcepub fn get(&self) -> &T
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!");