How to structure state objects in Rust?

Viewed 131

I have an app which has a dozen or more state fields.

struct State {
    a: u8,
    b: u16,
    c: u32,
    d: u64,
    ...
}

Most of these only apply when in certain states, though some may appear in all:

struct StateOne {
    a: u8,
    b: u16,
    ...
}

struct StateTwo {
    a: u8,
    c: u32,
    ...
}

struct StateThree {
    a: u8,
    c: u32,
    d: u64,
    ...
}

struct StateFour ...

I've already got into a huge mess by trying:

enum State {
    StateOne { a: u8, b: u16, ... },
    StateTwo { a: u8, c: u32, ... },
    StateThree { a: u8, c: u32, d: u64, ... },
    ...
}

The problems were related to having to manually match and dispatch N different methods to M different states, from impl State { .... This may be practical with Enum Variant Types in future.

There was also the issue that state transformation methods had to return a state, in case they needed to construct a new enum variant. It is preferable for them to take an &mut State (or &mut self) argument.

(I need to make new states by applying messages to states. I would like to be able to specify that particular messages only applies to particular states.)

I've read: https://blog.yoshuawuyts.com/state-machines/#state-machines-in-rust-today but 99% of messages only mutate valuess in the current state. It is exceptional that the state changes. The focus of that article is that state changes are the important thing.

I'm next tempted to make a SuperState struct:

struct SuperState {
    a: u8,
    b: Option<u16>,
    c: Option<u32>,
    d: Option<u64>,
    ...
}

enum State {
    One,
    Two,
    Three,
    ...
    Bad,
}

impl SuperState {
    pub fn state_flag(&self) -> State {
        match (self.b, self.c, self.d) {
            (Some(b), None, None) => State::One,
            (None, Some(c), None) => State::Two,
            (None, Some(c), Some(d)) => State::Three,
            ...
            _ => State::Bad,
        }
}

Here I will need to enforce invariants at runtime, which does not seem as nice, when compared to enums with strict sets of fields.

Is this worth trying, or are there obvious issues here too?

Are there other state patterns which are better?

0 Answers
Related