Skip to content

Instantly share code, notes, and snippets.

@spacejam
Created September 25, 2023 09:15
Show Gist options
  • Save spacejam/100e3e9dd6bf89a11f367a95597602f8 to your computer and use it in GitHub Desktop.
Save spacejam/100e3e9dd6bf89a11f367a95597602f8 to your computer and use it in GitHub Desktop.
compile-time state transitions
// states:
// A can transition to B.
// B can transition to either A or End.
// End terminates.
#[derive(Debug)]
struct A;
#[derive(Debug)]
struct B;
#[derive(Debug)]
struct End;
// events
#[derive(Debug)]
struct Alternate;
#[derive(Debug)]
struct Terminate;
#[derive(Debug)]
enum Event {
Alternate(Alternate),
Terminate(Terminate),
}
// concrete state machine without typestate
#[derive(Debug)]
enum SM {
A(A),
B(B),
End(End),
}
// define valid transitions with From(state, event) -> state
trait Transition<Event, NextState> {
fn transition(self, event: Event) -> NextState;
}
impl Transition<Alternate, B> for A {
fn transition(self, _: Alternate) -> B {
B
}
}
impl Transition<Alternate, A> for B {
fn transition(self, _: Alternate) -> A {
A
}
}
impl Transition<Terminate, End> for B {
fn transition(self, _: Terminate) -> End {
End
}
}
// boilerplate to lift it back into the SM
impl From<A> for SM {
fn from(input: A) -> SM {
SM::A(input)
}
}
impl From<B> for SM {
fn from(input: B) -> SM {
SM::B(input)
}
}
impl From<End> for SM {
fn from(input: End) -> SM {
SM::End(input)
}
}
// run and get the benefits of strong type transitions and dynamism
fn main() {
use rand::Rng;
let mut rng = rand::thread_rng();
let mut sm: SM = SM::A(A);
let mut events = vec![];
for _ in 0..10 {
if rng.gen_bool(0.8) {
events.push(Event::Alternate(Alternate));
} else {
events.push(Event::Terminate(Terminate));
}
}
for event in events.into_iter() {
dbg!(&sm);
sm = match (sm, event) {
(SM::A(a), Event::Alternate(alternate)) => SM::from(Transition::transition(a, alternate)),
(SM::B(b), Event::Alternate(alternate)) => SM::from(Transition::transition(b, alternate)),
(SM::B(b), Event::Terminate(terminate)) => SM::from(Transition::transition(b, terminate)),
(SM::End(_), _) => {
// this does not compile
// SM::from(Transition::transition(e, Alternate))
return;
},
(sm, e) => {
eprintln!("rejecting state:event combination: {:?}:{:?}", sm, e);
sm
},
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment