pub struct StateCell<State: StateMarker> { /* private fields */ }Expand description
Lock-free snapshot cell with versioning.
Internally uses ArcSwap for atomic snapshot replacement
and an AtomicU64 sequence counter for change detection.
Readers can cheaply peek or check if the snapshot changed without blocking writers.
§Internal Mutation
States can implement internal mutability (via RwLock, Mutex, atomics)
and use mutate_internal to modify data without
replacing the entire snapshot. The sequence counter still increments,
notifying observers of changes.
§Example: Full Snapshot Replacement
#[derive(Default)]
struct Config {
timeout: u64,
}
impl StateMarker for Config {}
let cell = StateCell::new(Config { timeout: 100 });
cell.publish(Config { timeout: 200 }); // Replace entire state§Example: Internal Mutation
use std::sync::{Arc, RwLock};
struct Metrics {
counters: Arc<RwLock<HashMap<String, u64>>>,
}
impl StateMarker for Metrics {}
let cell = StateCell::new(Metrics { ... });
// Modify without cloning the entire state
cell.mutate_internal(|state| {
let mut counters = state.counters.write().unwrap();
*counters.entry("requests".into()).or_insert(0) += 1;
});
// Observers detect the change via seq
if cell.changed_since(last_seq) {
println!("Metrics updated!");
}Implementations§
Source§impl<S: StateMarker> StateCell<S>
impl<S: StateMarker> StateCell<S>
Sourcepub fn new_default() -> Arc<Self>
pub fn new_default() -> Arc<Self>
Create a new cell with the state’s default value, wrapped in Arc.
Sourcepub fn publish_arc(&self, next: Arc<S>)
pub fn publish_arc(&self, next: Arc<S>)
Publish a new snapshot (by Arc).
Sourcepub fn peek(&self) -> Guard<Arc<S>>
pub fn peek(&self) -> Guard<Arc<S>>
Provides a temporary borrow of the object inside.
This returns a proxy object allowing access to the thing held inside. However, there’s
only limited amount of possible cheap proxies in existence for each thread ‒ if more are
created, it falls back to equivalent of load internally.
This is therefore a good choice to use for eg. searching a data structure or juggling the pointers around a bit, but not as something to store in larger amounts. The rule of thumb is this is suited for local variables on stack, but not in long-living data structures.
§Consistency
In case multiple related operations are to be done on the loaded value, it is generally
recommended to call peek just once and keep the result over calling it multiple times.
First, keeping it is usually faster. But more importantly, the value can change between the
calls to peek, returning different objects, which could lead to logical inconsistency.
Keeping the result makes sure the same object is used.
Sourcepub fn peek_if_changed(&self, last_seq: &mut u64) -> Option<Guard<Arc<S>>>
pub fn peek_if_changed(&self, last_seq: &mut u64) -> Option<Guard<Arc<S>>>
Load a snapshot only if sequence changed since last_seq.
Updates last_seq if changed.
Sourcepub fn with<R>(&self, f: impl FnOnce(&S, u64) -> R) -> R
pub fn with<R>(&self, f: impl FnOnce(&S, u64) -> R) -> R
Apply a closure to the current snapshot and sequence.
Sourcepub fn with_if_changed<R>(
&self,
last_seq: &mut u64,
f: impl FnOnce(&S) -> R,
) -> Option<R>
pub fn with_if_changed<R>( &self, last_seq: &mut u64, f: impl FnOnce(&S) -> R, ) -> Option<R>
Apply a closure if the sequence changed since last_seq.
Updates last_seq if changed.
Sourcepub fn changed_since(&self, last: u64) -> bool
pub fn changed_since(&self, last: u64) -> bool
Check if sequence changed since last.
Sourcepub fn mutate_internal<F>(&self, f: F)
pub fn mutate_internal<F>(&self, f: F)
Modify internal state without replacing the snapshot.
This method allows calling a closure that can mutate the state’s
internal data (e.g., via RwLock, Mutex, or atomics) without
creating a new Arc snapshot. The sequence counter is incremented
to notify observers of the change.
§Example
// State with internal mutability
struct MyState {
data: Arc<RwLock<Vec<String>>>,
}
let cell = StateCell::new(MyState { ... });
// Modify without replacing snapshot
cell.mutate_internal(|state| {
let mut data = state.data.write().unwrap();
data.push("new item".to_string());
});Sourcepub fn mutate_internal_with<F, R>(&self, f: F) -> R
pub fn mutate_internal_with<F, R>(&self, f: F) -> R
Modify internal state and return a result.
Similar to mutate_internal, but allows
the closure to return a value.
§Example
let count = cell.mutate_internal_with(|state| {
let mut data = state.data.write().unwrap();
data.push("item".to_string());
data.len()
});Trait Implementations§
Auto Trait Implementations§
impl<State> !Freeze for StateCell<State>
impl<State> RefUnwindSafe for StateCell<State>where
State: RefUnwindSafe,
impl<State> Send for StateCell<State>
impl<State> Sync for StateCell<State>
impl<State> Unpin for StateCell<State>
impl<State> UnwindSafe for StateCell<State>where
State: RefUnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request