rust

Is there a way to mutate the value in a mutex from StateMachine<Init> to StateMachine<Run>


I have written a state machine in Rust. I would now like to access it from several threads.

There are methods that change the state of the machine: e.g. from StateMachine<Init> to StateMachine<Run>

If wrapped in a mutex I receive errors when changing the state (mismatched types). And when assigning the state machine to a new variable I get an error too (cannot move out of dereference ..).

And I know that StateMachin<Init> is not the same typ as StateMachine<Run> but I am wondering if there is a workaround / solution so that I can use the state pattern in an asynchronous context?

use std::sync::Arc;

use tokio::sync::Mutex;

pub trait State {}

pub struct Init {}
impl State for Init {}

pub struct Run {}
impl State for Run {}

struct StateMachine<S>
where
    S: State,
{
    state: std::marker::PhantomData<S>,
}

impl<S> StateMachine<S>
where
    S: State,
{
    pub fn new() -> StateMachine<Init> {
        StateMachine {
            state: std::marker::PhantomData,
        }
    }
}

impl StateMachine<Init> {
    pub fn run(self) -> StateMachine<Run> {
        StateMachine {
            state: std::marker::PhantomData,
        }
    }
}

#[tokio::main]
async fn main() {
    let state_machine = Arc::new(Mutex::new(StateMachine::<Init>::new()));
    let locked_machine = state_machine.lock().await;

    //locked_machine = locked_machine.run();
    let new_state = locked_machine.run();
    let state_machine = Arc::new(Mutex::new(new_state));
}

I expect / hope that there is a way to make the mutex accept all StateMachine or that I receive the confirmation that what I want to do is impossible. I think that I understand why I get the errors but as of now I relly have no idea how to solve it.

I am quite new to Rust and a total newbie to anything async related. I tried to solve the issue with trait objects but I failed quite hard. I might try that again but I wasn't able to convert the trait object back to a specific struct e.g. StateMachine.

Another solution would be to implement the state machine as enum and to do the checks at runtime.

Thanks in advance for all help.


Solution

  • By definition you can't store all states of a type-based state machine, because they're different types.

    In order to do that you need to erase the type e.g. add an intermediate enum, or make trait extend Any and add support for dynamic state management.

    Another solution would be to implement the state machine as enum and to do the checks at runtime.

    Yes, if you always need to store the machine in multiple possible states then a typed state machine doesn't make sense. A typed state machine either doesn't escape, or it is stored in different objects / locations in each state.