pub struct SharedState<P, S, T>where
S: SharedStorage,{ /* private fields */ }Expand description
Shared state using an explicit, replaceable storage backend.
SharedState is the runtime boundary for this library. Owned state tokens
carry their current state only in the type system. Shared containers such
as Rc<RefCell<_>>, Arc<Mutex<_>>, and Arc<RwLock<_>> need one
authoritative runtime marker because aliases can request typed views at
different times.
A borrow checks that runtime marker first. After the check succeeds, the
returned value is again a statically typed State view, so ordinary
read-only state-machine methods regain compile-time guarantees:
use magicstatemachines::{SArcMutex, transition};
use test_def::{Online, states::{Connected, Disconnected}};
let shared = SArcMutex::<Connection>::new::<Disconnected>(
Connection::new("localhost:8080"),
);
{
let disconnected = shared.borrow_mut::<Disconnected>()?;
let connected = transition!(disconnected);
drop(connected); // commits `Connected` back to the shared container.
}
let connected = shared.borrow::<Connected>()?;
let online = shared.borrow::<Online>()?;The storage backend is an explicit type parameter. The built-in aliases cover the common cases:
SRcRefCell<T>for single-threaded shared mutable state;SArcMutex<T>for shared state protected bystd::sync::Mutex;SArcRwLock<T>for shared state protected bystd::sync::RwLock;SRc<Storage, T>andSArc<Storage, T>when you provide a customSharedStorageimplementation.
Union markers can be borrowed, but cannot be stored as the committed runtime state:
use magicstatemachines::{SArcMutex, StateMachineDefinition, StateMachineImpl, States};
struct Machine;
struct Standin;
States! {
A;
B;
}
StateMachineDefinition! {
for Standin;
pub Initial: A;
transition A => B();
union Any: A | B;
}
StateMachineImpl! {
Machine: Standin;
transition A => B();
}
let _state = SArcMutex::<Machine>::new::<Any>(Machine);Implementations§
Sourcepub fn downgrade(&self) -> WeakSRc<Storage, T>
pub fn downgrade(&self) -> WeakSRc<Storage, T>
Creates a weak handle to this Rc-backed shared state.
The weak handle remembers the same SharedStorage backend as the strong
handle. After upgrade, callers use the normal borrow and borrow_mut
APIs, so state mismatches and backend borrow errors are still reported by
those APIs:
let shared = SRcRefCell::<Connection>::new::<Disconnected>(connection);
let weak: WeakSRcRefCell<Connection> = shared.downgrade();
let shared = weak.upgrade().expect("at least one strong handle remains");
let disconnected = shared.borrow::<Disconnected>()?;Sourcepub fn downgrade(&self) -> WeakSArc<Storage, T>
pub fn downgrade(&self) -> WeakSArc<Storage, T>
Creates a weak handle to this Arc-backed shared state.
Upgrade failure means all strong Arc handles were dropped. It is not a
state-machine error and it is independent from wrong-state or lock errors:
let shared = SArcMutex::<Connection>::new::<Disconnected>(connection);
let weak: WeakSArcMutex<Connection> = shared.downgrade();
if let Some(shared) = weak.upgrade() {
let disconnected = shared.borrow::<Disconnected>()?;
}Sourcepub fn new<State>(value: T) -> Self
pub fn new<State>(value: T) -> Self
Creates shared state from a runtime value in an allowed initial state.
State must be a concrete initial state declared by the definition
crate. Union markers are intentionally rejected as committed storage
states; they can be borrowed as views after a concrete state is stored.
let shared = SArcMutex::<Connection>::new::<Disconnected>(
Connection::new("localhost:8080"),
);Sourcepub fn from_state<StateMarker>(state: State<SOwned, T, StateMarker>) -> Selfwhere
StateMarker: ConcreteStateTrait,
pub fn from_state<StateMarker>(state: State<SOwned, T, StateMarker>) -> Selfwhere
StateMarker: ConcreteStateTrait,
Moves an owned state token into shared storage without changing state.
This is the shared-storage counterpart to putting an already-created
State into Rc, Arc, or another container. The committed runtime
marker is taken from the concrete state token.
let disconnected: State<SOwned, Connection, Disconnected> =
State::new(Connection::new("localhost:8080"));
let shared = SArcMutex::<Connection>::from_state(disconnected);Sourcepub fn borrow<RequestedState>(
&self,
) -> Result<SRefView<'_, Backend, T, RuntimeStateMarker<RequestedState>>, SharedStateError<Backend::ReadError<'_, T>>>where
RequestedState: StateTrait + StateMarker + StateRuntimeMarkerFor<<RequestedState as StateMarker>::Kind>,
RuntimeStateMarker<RequestedState>: SharedBorrowState,
pub fn borrow<RequestedState>(
&self,
) -> Result<SRefView<'_, Backend, T, RuntimeStateMarker<RequestedState>>, SharedStateError<Backend::ReadError<'_, T>>>where
RequestedState: StateTrait + StateMarker + StateRuntimeMarkerFor<<RequestedState as StateMarker>::Kind>,
RuntimeStateMarker<RequestedState>: SharedBorrowState,
Borrows the runtime value if the committed state matches RequestedState.
RequestedState may be a concrete state or a generated union marker.
Concrete borrows require the exact committed state. Union borrows
succeed when the committed concrete state is a member of that union.
Errors distinguish “the container could not be borrowed/locked” from
“the borrow succeeded but the state was wrong”:
let connected = shared.borrow::<Connected>()?;
let online = shared.borrow::<Online>()?;
match shared.borrow::<Authenticated>() {
Err(magicstatemachines::SharedStateError::WrongState(error)) => {
eprintln!("{error}");
}
other => { /* storage errors and success are handled separately */ }
}Sourcepub fn borrow_mut<RequestedState>(
&self,
) -> Result<SMutView<'_, Backend, T, RuntimeStateMarker<RequestedState>>, SharedStateError<Backend::WriteError<'_, T>>>where
RequestedState: StateTrait + StateMarker + StateRuntimeMarkerFor<<RequestedState as StateMarker>::Kind>,
RuntimeStateMarker<RequestedState>: SharedBorrowState,
pub fn borrow_mut<RequestedState>(
&self,
) -> Result<SMutView<'_, Backend, T, RuntimeStateMarker<RequestedState>>, SharedStateError<Backend::WriteError<'_, T>>>where
RequestedState: StateTrait + StateMarker + StateRuntimeMarkerFor<<RequestedState as StateMarker>::Kind>,
RuntimeStateMarker<RequestedState>: SharedBorrowState,
Mutably borrows the runtime value and tracks the guard’s final state.
When the returned guard is dropped, the shared container is updated to
the guard’s pending state. This allows methods on State<SMutView<...>>
to retain compile-time transition checks while the committed state is
stored at runtime.
{
let connected = shared.borrow_mut::<Connected>()?;
let authenticated = transition!(connected, "alice".to_owned());
drop(authenticated); // commits `Authenticated`.
}
let authenticated = shared.borrow::<Authenticated>()?;