State machine in Rust


#1

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 ?


#2

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.


#3

@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

#4

You can make the ProfileStateMachine struct generic over the Guard and Handler types: playground.


#5

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.