pub struct State<Storage, T, S>where
T: StateMachineImpl,
Storage: StateStorage,{ /* private fields */ }Expand description
A state token parameterized by storage, runtime implementation, and state.
The type parameters carry the whole proof:
Storageselects how the runtime value is held, for exampleSOwned,StorageStateOwnedBox, or a mutable guard from shared storage.Tis the runtime implementation type, such asConnection.Sis the current state marker, such asDisconnectedorimpl InOnline.
State is intentionally a deref-like wrapper with very few inherent
methods. State-machine behavior should be implemented on T with
arbitrary self types, not added as generic convenience methods on State.
That keeps the transition capability private to the implementation module.
impl Connection {
fn connect<S>(self: State<S, Self, Disconnected>) -> State<S, Self, Connected>
where
S: SMut,
{
magicstatemachines::transition!(self)
}
fn endpoint<S>(self: &State<S, Self, impl InOnline>) -> &str
where
S: SRef,
{
&self.endpoint
}
}Implementations§
Source§impl<T, S> State<StorageStateOwned, T, S>where
T: StateMachineImpl,
impl<T, S> State<StorageStateOwned, T, S>where
T: StateMachineImpl,
Sourcepub fn new(value: T) -> Self
pub fn new(value: T) -> Self
Wraps an implementation in an initial directly owned state.
This is the normal entry point for a state machine. It only compiles
when the definition crate declared S as an initial state for
T::Standin.
use magicstatemachines::{SOwned, State};
use test_def::states::Disconnected;
let disconnected: State<SOwned, Connection, Disconnected> =
State::new(Connection::new("localhost:8080"));To move the state into another owned container later, create that
container from the returned state token instead of constructing raw
T again.
Source§impl<T, S> State<StorageStateOwnedBox, T, S>where
T: StateMachineImpl,
impl<T, S> State<StorageStateOwnedBox, T, S>where
T: StateMachineImpl,
Sourcepub fn new(state: State<StorageStateOwned, T, S>) -> Self
pub fn new(state: State<StorageStateOwned, T, S>) -> Self
Moves a directly owned state into Box storage without changing its state.
This mirrors Box::new, but it takes State<SOwned, T, S> instead of
raw T so the current state is preserved:
let owned: State<SOwned, Connection, Connected> = connection.connect();
let boxed: SBox<Connection, Connected> = SBox::new(owned);Sourcepub fn unbox(state: Self) -> State<StorageStateOwned, T, S>
pub fn unbox(state: Self) -> State<StorageStateOwned, T, S>
Moves this boxed state back into direct owned storage.
This consumes the box and returns State<SOwned, T, S>. The state
marker is unchanged, so methods available before boxing remain
available after unboxing.
let boxed: SBox<Connection, Connected> = SBox::new(owned);
let owned_again: State<SOwned, Connection, Connected> = SBox::unbox(boxed);Source§impl<T, S> State<StorageStateOwnedPinBox, T, S>where
T: StateMachineImpl,
impl<T, S> State<StorageStateOwnedPinBox, T, S>where
T: StateMachineImpl,
Sourcepub fn new(state: State<StorageStateOwnedBox, T, S>) -> Self
pub fn new(state: State<StorageStateOwnedBox, T, S>) -> Self
Pins an already boxed state in place without changing its state.
Pinning must happen to the boxed allocation. For that reason this
constructor consumes SBox rather than SOwned:
let boxed: SBox<Connection, Connected> = SBox::new(owned);
let pinned: SPinBox<Connection, Connected> = SPinBox::new(boxed);Sourcepub fn into_boxed(state: Self) -> State<StorageStateOwnedBox, T, S>where
T: Unpin,
pub fn into_boxed(state: Self) -> State<StorageStateOwnedBox, T, S>where
T: Unpin,
Converts pinned box storage back to box storage when the runtime is Unpin.
This is only available for T: Unpin; otherwise the pinning guarantee
must be preserved.
use core::marker::PhantomPinned;
use magicstatemachines::{
Initial, SBox, SOwned, SPinBox, State, StateMachineImpl, States,
};
struct Machine;
struct Runtime {
_pin: PhantomPinned,
}
struct Token;
States! {
Ready;
}
impl Initial<Ready> for Machine {}
impl StateMachineImpl for Runtime {
type Standin = Machine;
type Impl = Self;
type TransitionToken = Token;
}
let ready = State::<SOwned, _, Ready>::new(Runtime {
_pin: PhantomPinned,
});
let pinned: SPinBox<Runtime, Ready> = SPinBox::new(SBox::new(ready));
// `Runtime` is `!Unpin`, so the pinned allocation cannot be turned
// back into an ordinary box.
let _boxed = SPinBox::into_boxed(pinned);Source§impl<Storage, T, Marker> State<SDiscriminated<Storage>, T, StateUnionState<Marker>>
impl<Storage, T, Marker> State<SDiscriminated<Storage>, T, StateUnionState<Marker>>
Sourcepub fn discriminate(
self,
) -> <Marker as StateUnionDiscriminant>::Enum<Storage, T>
pub fn discriminate( self, ) -> <Marker as StateUnionDiscriminant>::Enum<Storage, T>
Recovers the generated enum for this discriminated union state.
This is the runtime branch point: after matching on the enum, each variant can be converted back into its concrete state.
match online.discriminate() {
OnlineEnum::Connected(connected) => {
let authenticated = connected.authenticate("alice");
}
OnlineEnum::Authenticated(authenticated) => {
let connected = authenticated.logout();
}
}