Is there any state machine library similar to ruby state_machine gem or is there a way by which it can be easily achieved in Rust ?
1 Like
I've heard that the amethyst game engine uses a pushdown automaton for state management. Not sure if it's their own or if they use a commonly known crate, but you could take a look.
Edit: Here's a design doc for their state machine implementation.
@ArmsOfSorrow I was thinking to implement something like this
#[derive(PartialEq)]
pub enum ProfileStatus {
Active,
Deactivated,
}
#[derive(PartialEq)]
enum ProfileEvent {
Register,
}
struct Transition<E, S, G, H>{
event : E,
from : S,
to : S,
guard : G ,
before_handler : Option<H>,
on_handler : H,
after_handler : Option<H>,
}
trait Guard<C : Context> {
fn canTransition(&self, &C) -> bool;
}
struct TrueGuard;
impl Guard<ProfileContext> for TrueGuard {
fn canTransition(&self, p : &ProfileContext) -> bool {
return true
}
}
trait Handler<C : Context>{
fn handle(&self, &C) -> ();
}
struct ProfileHandler;
impl Handler<ProfileContext> for ProfileHandler {
fn handle(&self, p : &ProfileContext) -> () {
}
}
trait Context {}
struct ProfileContext;
impl Context for ProfileContext {}
trait StateMachine<E : PartialEq, S : PartialEq, G : Guard<C>, H : Handler<C>, C : Context> {
fn getTransitions(&self) -> Vec<Transition<E,S,G,H>>;
fn addTransition(&mut self, t : Transition<E,S,G,H>);
fn transition(&self, event : E, fromState: S, toState: S, context : C) {
self.getTransitions().into_iter()
.filter(|t| t.event == event && t.from == fromState && t.to == toState && t.guard.canTransition(&context))
.take(1)
.for_each( |t| {
if t.before_handler.is_some() {
t.before_handler.unwrap().handle(&context);
}
t.on_handler.handle(&context);
if t.after_handler.is_some() {
t.after_handler.unwrap().handle(&context);
}
});
}
}
struct ProfileStateMachine {
t : Vec<Transition<ProfileEvent, ProfileStatus, Guard<ProfileContext>, Handler<ProfileContext>>>,
}
fn main(){
}
I am getting following error however
error[E0277]: the trait bound `Guard<ProfileContext> + 'static: std::marker::Sized` is not satisfied
--> state_machine_3.rs:73:5
|
73 | t : Vec<Transition<ProfileEvent, ProfileStatus, Guard<ProfileContext>, Handler<ProfileContext>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Guard<ProfileContext> + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Guard<ProfileContext> + 'static`
= note: required by `Transition`
error[E0277]: the trait bound `Handler<ProfileContext> + 'static: std::marker::Sized` is not satisfied
--> state_machine_3.rs:73:5
|
73 | t : Vec<Transition<ProfileEvent, ProfileStatus, Guard<ProfileContext>, Handler<ProfileContext>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Handler<ProfileContext> + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Handler<ProfileContext> + 'static`
= note: required by `Transition`
error: aborting due to 2 previous errors
You can make the ProfileStateMachine
struct generic over the Guard and Handler types: playground.
For usage with future's, there is https://crates.io/crates/state_machine_future.
It's also possible for a machine to be wrapped as a state of an outer machine.