pub struct Container<K: Kind> { /* private fields */ }
Expand description
A container for global type-based state.
A container can store at most one instance of given type as well as n thread-local instances of a given type.
Type Bounds
A Container
can store values that are both Send + Sync
, just Send
, or
neither. The Container!
macro is used to specify the type of container:
use state::Container;
// Values must implement `Send + Sync`. The container itself is `Send + Sync`.
let container: Container![Send + Sync] = <Container![Send + Sync]>::new();
let container: Container![Sync + Send] = <Container![Sync + Send]>::new();
// Values must implement `Send`. The container itself is `Send`, `!Sync`.
let container: Container![Send] = <Container![Send]>::new();
// Values needn't implement `Send` nor `Sync`. `Container` is `!Send`, `!Sync`.
let container: Container![] = <Container![]>::new();
Setting State
Global state is set via the set()
method and retrieved
via the get()
method. The type of the value being set
must meet the bounds of the Container
.
use state::Container;
fn f_send_sync<T: Send + Sync + Clone + 'static>(value: T) {
let container = <Container![Send + Sync]>::new();
container.set(value.clone());
let container = <Container![Send]>::new();
container.set(value.clone());
let container = <Container![]>::new();
container.set(value.clone());
}
fn f_send<T: Send + Clone + 'static>(value: T) {
// This would fail to compile since `T` may not be `Sync`.
// let container = <Container![Send + Sync]>::new();
// container.set(value.clone());
let container = <Container![Send]>::new();
container.set(value.clone());
let container = <Container![]>::new();
container.set(value.clone());
}
fn f<T: 'static>(value: T) {
// This would fail to compile since `T` may not be `Sync` or `Send`.
// let container = <Container![Send + Sync]>::new();
// container.set(value.clone());
// This would fail to compile since `T` may not be `Send`.
// let container = <Container![Send]>::new();
// container.set(value.clone());
let container = <Container![]>::new();
container.set(value);
}
// If `Container` is `Send + Sync`, it can be `const`-constructed.
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
CONTAINER.set(String::new());
CONTAINER.get::<String>();
Freezing
By default, all get
, set
, get_local
, and set_local
calls result in
synchronization overhead for safety. However, if calling set
or
set_local
is no longer required, the overhead can be eliminated by
freezing the Container
. A frozen container can only be read and never
written to. Attempts to write to a frozen container will be ignored.
To freeze a Container
, call freeze()
. A frozen
container can never be thawed. To check if a container is frozen, call
is_frozen()
.
Thread-Local State
Thread-local state on a Send + Sync
container is set via the
set_local()
method and retrieved via the
get_local()
method. The type of the value being
set must be transferable across thread boundaries but need not be
thread-safe. In other words, it must satisfy Send + 'static
but not
necessarily Sync
. Values retrieved from thread-local state are exactly
that: local to the current thread. As such, you cannot use thread-local
state to synchronize across multiple threads.
Thread-local state is initialized on an as-needed basis. The function used
to initialize the thread-local state is passed in as an argument to
set_local
. When the state is retrieved from a given thread for the first
time, the function is executed to generate the initial value. The function
is executed at most once per thread. The same function is used for
initialization across all threads.
Note: Rust reuses thread IDs across multiple threads. This means that is possible to set thread-local state in thread A, have that thread die, start a new thread B, and access the state set in tread A in thread B.
Example
Set and later retrieve a value of type T:
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
CONTAINER.set_local(|| T::new());
CONTAINER.get_local::<T>();
Implementations
sourceimpl Container<SendSync>
impl Container<SendSync>
sourcepub const fn new() -> Self
pub const fn new() -> Self
Creates a new container with no stored values.
Example
Create a globally available state container:
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
sourcepub fn set<T: Send + Sync + 'static>(&self, state: T) -> bool
pub fn set<T: Send + Sync + 'static>(&self, state: T) -> bool
Sets the global state for type T
if it has not been set before and
self
is not frozen.
If the state for T
has previously been set or self
is frozen, the
state is unchanged and false
is returned. Otherwise true
is
returned.
Example
Set the state for AtomicUsize
. The first set
is succesful while the
second fails.
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
assert_eq!(CONTAINER.set(AtomicUsize::new(0)), true);
assert_eq!(CONTAINER.set(AtomicUsize::new(1)), false);
sourcepub fn set_local<T, F>(&self, state_init: F) -> bool where
T: Send + 'static,
F: Fn() -> T + Send + Sync + 'static,
pub fn set_local<T, F>(&self, state_init: F) -> bool where
T: Send + 'static,
F: Fn() -> T + Send + Sync + 'static,
Sets the thread-local state for type T
if it has not been set before.
The state for type T
will be initialized via the state_init
function as
needed. If the state for T
has previously been set, the state is unchanged
and false
is returned. Returns true
if the thread-local state is
successfully set to be initialized with state_init
.
Example
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
struct MyState(Cell<usize>);
assert_eq!(CONTAINER.set_local(|| MyState(Cell::new(1))), true);
assert_eq!(CONTAINER.set_local(|| MyState(Cell::new(2))), false);
sourcepub fn try_get_local<T: Send + 'static>(&self) -> Option<&T>
pub fn try_get_local<T: Send + 'static>(&self) -> Option<&T>
Attempts to retrieve the thread-local state for type T
.
Returns Some
if the state has previously been set via
set_local. Otherwise returns None
.
Example
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
struct MyState(Cell<usize>);
CONTAINER.set_local(|| MyState(Cell::new(10)));
let my_state = CONTAINER.try_get_local::<MyState>().expect("MyState");
assert_eq!(my_state.0.get(), 10);
sourcepub fn get_local<T: Send + 'static>(&self) -> &T
pub fn get_local<T: Send + 'static>(&self) -> &T
Retrieves the thread-local state for type T
.
Panics
Panics if the thread-local state for type T
has not previously been set
via set_local. Use
try_get_local for a non-panicking version.
Example
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
struct MyState(Cell<usize>);
CONTAINER.set_local(|| MyState(Cell::new(10)));
let my_state = CONTAINER.get_local::<MyState>();
assert_eq!(my_state.0.get(), 10);
sourceimpl Container<Send>
impl Container<Send>
sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new container with no stored values.
Example
use std::cell::Cell;
use state::Container;
let container = <Container![Send]>::new();
let value: Cell<u8> = Cell::new(10);
container.set(value);
assert_eq!(container.get::<Cell<u8>>().get(), 10);
container.get::<Cell<u8>>().set(99);
assert_eq!(container.get::<Cell<u8>>().get(), 99);
sourcepub fn set<T: Send + 'static>(&self, state: T) -> bool
pub fn set<T: Send + 'static>(&self, state: T) -> bool
Sets the global state for type T
if it has not been set before and
self
is not frozen.
If the state for T
has previously been set or self
is frozen, the
state is unchanged and false
is returned. Otherwise true
is
returned.
Example
Set the state. The first set
is succesful while the second fails.
use state::Container;
let container = <Container![Send]>::new();
assert!(container.set(AtomicUsize::new(0)));
assert!(!container.set(AtomicUsize::new(1)));
sourceimpl Container<Neither>
impl Container<Neither>
sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new container with no stored values.
Example
use std::cell::Cell;
use state::Container;
let container = <Container![]>::new();
let value: Cell<u8> = Cell::new(10);
container.set(value);
assert_eq!(container.get::<Cell<u8>>().get(), 10);
container.get::<Cell<u8>>().set(99);
assert_eq!(container.get::<Cell<u8>>().get(), 99);
sourcepub fn set<T: 'static>(&self, state: T) -> bool
pub fn set<T: 'static>(&self, state: T) -> bool
Sets the global state for type T
if it has not been set before and
self
is not frozen.
If the state for T
has previously been set or self
is frozen, the
state is unchanged and false
is returned. Otherwise true
is
returned.
Example
Set the state. The first set
is succesful while the second fails.
use std::cell::Cell;
use state::Container;
let container = <Container![]>::new();
assert!(container.set(Cell::new(10)));
assert!(!container.set(Cell::new(17)));
sourceimpl<K: Kind> Container<K>
impl<K: Kind> Container<K>
sourcepub fn try_get<T: 'static>(&self) -> Option<&T>
pub fn try_get<T: 'static>(&self) -> Option<&T>
Attempts to retrieve the global state for type T
.
Returns Some
if the state has previously been set.
Otherwise returns None
.
Example
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
struct MyState(AtomicUsize);
// State for `T` is initially unset.
assert!(CONTAINER.try_get::<MyState>().is_none());
CONTAINER.set(MyState(AtomicUsize::new(0)));
let my_state = CONTAINER.try_get::<MyState>().expect("MyState");
assert_eq!(my_state.0.load(Ordering::Relaxed), 0);
sourcepub fn get<T: 'static>(&self) -> &T
pub fn get<T: 'static>(&self) -> &T
Retrieves the global state for type T
.
Panics
Panics if the state for type T
has not previously been
set. Use try_get for a non-panicking
version.
Example
use state::Container;
static CONTAINER: Container![Send + Sync] = <Container![Send + Sync]>::new();
struct MyState(AtomicUsize);
CONTAINER.set(MyState(AtomicUsize::new(0)));
let my_state = CONTAINER.get::<MyState>();
assert_eq!(my_state.0.load(Ordering::Relaxed), 0);
sourcepub fn freeze(&mut self)
pub fn freeze(&mut self)
Freezes the container. A frozen container disallows writes allowing for synchronization-free reads.
Example
use state::Container;
// A new container starts unfrozen and can be written to.
let mut container = <Container![Send + Sync]>::new();
assert_eq!(container.set(1usize), true);
// While unfrozen, `get`s require synchronization.
assert_eq!(container.get::<usize>(), &1);
// After freezing, calls to `set` or `set_local `will fail.
container.freeze();
assert_eq!(container.set(1u8), false);
assert_eq!(container.set("hello"), false);
// Calls to `get` or `get_local` are synchronization-free when frozen.
assert_eq!(container.try_get::<u8>(), None);
assert_eq!(container.get::<usize>(), &1);
sourcepub fn is_frozen(&self) -> bool
pub fn is_frozen(&self) -> bool
Returns true
if the container is frozen and false
otherwise.
Example
use state::Container;
// A new container starts unfrozen and is frozen using `freeze`.
let mut container = <Container![Send]>::new();
assert_eq!(container.is_frozen(), false);
container.freeze();
assert_eq!(container.is_frozen(), true);
sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of distinctly typed values in the containers.
Example
use state::Container;
let container = <Container![Send + Sync]>::new();
assert_eq!(container.len(), 0);
assert_eq!(container.set(1usize), true);
assert_eq!(container.len(), 1);
assert_eq!(container.set(2usize), false);
assert_eq!(container.len(), 1);
assert_eq!(container.set(1u8), true);
assert_eq!(container.len(), 2);
Trait Implementations
impl Send for Container<SendSync>
impl Send for Container<Send>
impl Sync for Container<SendSync>
Auto Trait Implementations
impl<K> !RefUnwindSafe for Container<K>
impl<K> !Send for Container<K>
impl<K> !Sync for Container<K>
impl<K> Unpin for Container<K> where
K: Unpin,
impl<K> UnwindSafe for Container<K> where
K: UnwindSafe,
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcefn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more