I'm beginner in rust. I would ask code review of my finite state machine. Main feature :
- static (event, state and transition)
- support pushdown
It is the first step, second step is to use macro for generate section.
In my example i have 3 events (e1, e2, e3), 4 states (red, green, blue , rainbow). There are 4 type of transitions :
- Move to other state with or without save origin state
- None to stay on same State
- Pop to restore the last save state
- Invalid to say invalid event on the state
static code :
use std::marker::PhantomData;
/**
* Static
*/
#[derive(Debug, Copy, Clone)]
enum Transition<S> {
None,
Invalid,
Move(S, bool),
Pop,
}
trait StateAction<E, S> {
fn push(&mut self, event: E) -> Transition<S>;
}
trait StateManager<E, S> {
fn get_state(&mut self, state: S) -> &mut dyn StateAction<E, S>;
}
struct StateMachine<M, E, S>
where
M: StateManager<E, S>,
S: Copy,
E: Copy,
{
manager: M,
current: S,
pop_state: Vec<S>,
phantom: PhantomData<E>,
}
impl<M, E, S> StateMachine<M, E, S>
where
M: StateManager<E, S>,
S: Copy,
E: Copy,
{
fn new(manager: M, state: S) -> Self {
Self {
manager: manager,
current: state,
pop_state: Vec::new(),
phantom: PhantomData,
}
}
fn push(&mut self, event: E) -> Transition<S> {
let current_state = self.manager.get_state(self.current);
let mut transition = current_state.push(event);
match transition {
Transition::Move(new_state, push) => {
if push {
self.pop_state.push(self.current);
}
self.current = new_state;
}
Transition::Pop => {
match self.pop_state.pop() {
Some(new_state) => {
self.current = new_state;
transition = Transition::Move(new_state, false);
},
None => panic!("invalid pop"),
}
}
_ => {}
}
transition
}
}
generated code :
#[derive(Debug, Copy, Clone)]
enum Event {
E1,
E2,
E3,
}
#[derive(Debug, Copy, Clone)]
enum State {
Green,
Red,
Blue,
Rainbow,
}
struct GreenState;
impl GreenState {
fn new() -> Self {
Self {}
}
}
impl StateAction<Event, State> for GreenState {
fn push(&mut self, event: Event) -> Transition<State> {
match event {
Event::E1 => Transition::Move(State::Red, false),
Event::E2 => Transition::Move(State::Blue, false),
Event::E3 => Transition::Move(State::Rainbow, true),
}
}
}
struct RedState;
impl RedState {
fn new() -> Self {
Self {}
}
}
impl StateAction<Event, State> for RedState {
fn push(&mut self, event: Event) -> Transition<State> {
match event {
Event::E1 => Transition::Move(State::Rainbow, true),
Event::E2 => Transition::Move(State::Blue, false),
Event::E3 => Transition::Move(State::Green, false),
}
}
}
struct BlueState;
impl BlueState {
fn new() -> Self {
Self {}
}
}
impl StateAction<Event, State> for BlueState {
fn push(&mut self, event: Event) -> Transition<State> {
match event {
Event::E1 => Transition::Move(State::Red, false),
Event::E2 => Transition::Move(State::Rainbow, true),
Event::E3 => Transition::Move(State::Green, false),
}
}
}
struct RainbowState;
impl RainbowState {
fn new() -> Self {
Self {}
}
}
impl StateAction<Event, State> for RainbowState {
fn push(&mut self, event: Event) -> Transition<State> {
match event {
Event::E1 => Transition::Pop,
Event::E2 => Transition::None,
Event::E3 => Transition::Invalid,
}
}
}
struct StateManagerImpl {
states: (GreenState, RedState, BlueState, RainbowState),
}
impl StateManagerImpl {
fn new() -> Self {
Self {
states: (
GreenState::new(),
RedState::new(),
BlueState::new(),
RainbowState::new(),
),
}
}
}
impl StateManager<Event, State> for StateManagerImpl {
fn get_state(&mut self, state: State) -> &mut dyn StateAction<Event, State> {
match state {
State::Green => &mut self.states.0,
State::Red => &mut self.states.1,
State::Blue => &mut self.states.2,
State::Rainbow => &mut self.states.3,
}
}
}
Example :
fn main() {
let manager = StateManagerImpl::new();
let mut sm = StateMachine::new(manager, State::Green);
{
// Green -> Red
let n = sm.push(Event::E1);
println!("after e1 {:?}", n);
}
{
// Red -> Blue
let n = sm.push(Event::E2);
println!("after e2 {:?}", n);
}
{
// Blue -> Rainbow
let n = sm.push(Event::E2);
println!("after e2 {:?}", n);
}
{
// Rainbow (stay)
let n = sm.push(Event::E2);
println!("after e2 {:?}", n);
}
{
// Rainbow (error)
let n = sm.push(Event::E3);
println!("after e3 {:?}", n);
}
{
// Rainbow/Pop -> Blue
let n = sm.push(Event::E1);
println!("after e3 {:?}", n);
}
}
the output:
after e1 Move(Red, false)
after e2 Move(Blue, false)
after e2 Move(Rainbow, true)
after e2 None
after e3 Invalid
after e3 Move(Blue, false)
what do you think ?
Thanks Etienne