5 ms·
Associated types are slightly different than generic types. trait State { type NextState; fn transition(self) -> Self::NextState; }
by unlinked_dll 6y ago
Associated types are slightly different than generic types.
trait State {
type NextState;
fn transition(self) -> Self::NextState;
}
In this example one consumes the current state to yield the next state (one could use From/TryFrom, but I don't think that makes sense semantically, imo).
The advantage of this approach is that if you design your State types such that they can only be constructed by transitioning from a previous state, you cannot write code where your program is accidentally in an invalid state. You also never need to match against an enum.
- lewisinc 6y agoYou should keep going with this and see how ergonomic it is when NextState can be one of many states. I tried implementing State Machines once with this method and ended up wanting to use an `enum` for `State::NextState`. After a while it ended up being pretty unwieldy.
- jstrong 6y agoI've had good luck in the past using From<T> and TryFrom<T> to go from State<A> -> State<B>. It's similar to what you propose insofar as there are only some transitions defined, just relying on the built-in traits instead of using your own. Since State::from(prev) is so much more ergonomic than the other ways you could manually construct an illegal transition, it's not much of a problem to prevent that in practice. But I would never use this approach without being able to generate code with macros, otherwise it'd be a pain. you mention "never have to match against an enum" - I think that's the biggest thing I learned from my experiences, is that generics or traits + associated types are much nicer to use than enums for the same purposes. With an enum, you have to match to pull any data out. It becomes annoying quickly.