Comment by vatsachak

4 hours ago

Why is it cumbersome?

The article also mentions that typestates can be cumbersome:

> Typestate improves code faultlessness and testability, but comes at the cost of more boilerplate code and can degrade readability.

I have noticed this in my own code. `Ticket` with an internal variable tracking the state makes using it simpler. I just have to store one object in my struct `struct MyData { ticket: Ticket }` and call `ticket` methods in the correct order.

Typestate `Ticket<T>` is not as simple. I have to wrap it in my own enum: `enum TicketState { Ticket1(Ticket<Func1Done>), Ticket2(Ticket<Func2Done>), }` to store in my struct: `struct MyData { ticket: TicketState }`. Then every time I call `ticket` methods, I must extract the correct variant value first. That degrades readability and creates extra run-time cost.

  • You don't need the enum? You just require Ticket<T_0> as a function argument.

    It's really not that cumbersome, it's like two extra lines of code...

I’m probably doing it wrong, but when there’s a state with multiple transitions out, I can either model it as distinct methods per transition in which case the caller needs to know how to transition between states or I can have the caller pass an enum in which moves the branch into the state machine at the expense of an enum and a match statement. It’s also like 10x the code. Again, I’m very open to the possibility that I’m doing something wrong. Curious how you would model a state machine for (1) reserving the right to do the inventory (2) querying the next page of results (based on a cursor) and (3) recording the page information and the next cursor.

  • In the type state pattern you would have something like this

    pub trait ValidState {}

    struct StateMachine<'a, T>

    where

      T: ValidState 

    {

    untyped: &'a mut UntypedStateMachine,

    _marker: PhantomData<T>

    }

    fn reserve_right<'a>(state: StateMachine<'a, Begin>) -> StateMachine<'a, Reserved>

    fn query<'a>(state: StateMachine<'a, Reserved>) -> StateMachine<'a, Queried>

    fn record<'a>(state: StateMachine<'a, Queried>) -> StateMachine<'a, Recorded>