6 ms·
A Fistful of States: More State Machine Patterns in Rust
- chrismorgan 6y agoA request about the page, rather than the content: please remove the preloader (element with ID preload), all it does is slow down page load and cause problems if the relevant JavaScript doesn’t load (whether by deliberate user choice or just because things don’t always work smoothly). There used to be reasons, however dubious, for such loading indicators, but those reasons are all roughly obsolete now.
- oflannabhra 6y agoOne of my favorite things about Swift is its rich enums that (similar to Rust) serve as the building blocks for state machines. Swift has the advantage of expressive switch statements with pattern matching that have allowed me to simplify and condense the "which state should I go to next given x" type of logic. I'm not trying to say Swift is better, by the way.
- estebank 6y agoHow is Swift's switch different to Rust's match?
- sullyj3 6y agoI don't think OP was necessarily claiming they were different
- oflannabhra 6y agoI'm not an expert in Rust, so some of these things might be available in it's match (just in a way I'm unfamiliar with), but for me the big advantages of Swift's switch are: - switching on tuples (not wrapped in a var): this prevents doubly-nested switches, which I've always found to be a problem in control logic. - unwrapping optionals - matching against an enum's associated value, and unwrapping it - using `where` clauses as part of the pattern - matching against type casts
- chewbacha 6y agoRust has all that functionality, but as the author mentioned, they avoided using enums and match statements because it led to more runtime errors and they were specifically trying to incur compile time errors. This is why they choose to use trait objects and type definitions to control state transitions. Edit: I invite you to learn more about the match keyword in rust and see how it relates to swift. I think you’ll find that it meets or exceeds your expectations: https://doc.rust-lang.org/book/ch06-02-match.html https://doc.rust-lang.org/book/ch06-02-match.html
- masklinn 6y agoAside from the last, this is all very standard stuff, I don't mean for Rust I mean for language with sum types in general. > - switching on tuples (not wrapped in a var): this prevents doubly-nested switches, which I've always found to be a problem in control logic match t { (Some(a), Some(b)) => a + b, (Some(a), None) => a, (None, Some(b)) => b, (None, None) => 0 } > - unwrapping optionals match opt { Some(v) => v, None => 0 } although it's common to use HOFs, the `?` operator, or `if let`. > - matching against an enum's associated value, and unwrapping it See above. - using `where` clauses as part of the pattern match opt { Some(v) if v > 5 => v - 5, Some(v) => v, None => 0 } > - matching against type casts Rust doesn't really do subtyping, so that's not a case which would come up. I guess the closest would be matching on the result of a downcast_ref (https://doc.rust-lang.org/std/any/trait.Any.html#method.downcast_ref https://doc.rust-lang.org/std/any/trait.Any.html#method.down...) but that just returns an Option so there's nothing special to it.
- discardable_dan 6y agoMostly match recurrence. One pain point about Rust is, due to its linear nature, you cannot use `as` patterns, such as: match e with x as (y, z) -> (x[0], x[1]) == (y, z) This is because the matcher itself must take ownership of the expression, and destructuring is an ownership transfer.
- kevincox 6y agoCan you point me to the swift docs for that? I don't understand what you are trying to do. You can bind references to what you are matching with @name. You also don't need to take ownership of a value. You can match on a reference to it with &. However without understanding your example it is hard to say for sure.
- masklinn 6y ago> You can bind references to what you are matching with @name. Given what they're saying I assume they're trying to match both the entire value and the sub-values e.g. xs@(_:xs') in Haskell. Stable Rust currently lets you use at-patterns and perform assertions on the sub-pattern, but not match there. > You also don't need to take ownership of a value. You can match on a reference to it with &. `ref` is what you'd use in a pattern though, `&` would deref' a reference so usually it'd be to Copy a Copy type which is provided behind a ref'.
- kevincox 6y ago> match both the entire value and the sub-values I see. Yes, it appears to be either-or for now. > `ref` is what you'd use in a pattern though Not in the pattern, in the "argument". Like this: https://rust.godbolt.org/z/eT366s https://rust.godbolt.org/z/eT366s pub fn f(v: &Option<String>) { match &v { other => eprintln!("Doesn't move {:?}", other), } }
- estebank 6y agoYou can do both. Your code is closer stylistically to what you'd want to use most of the time, but every now and then I encounter a case where using ref in a single pattern makes more sense.
- ragnese 6y agoThe only thing that that is bugging me at this very moment is that Swift's switch is a statement and not an expression. Ugh!
- amelius 6y agoWho is writing state machines without the pattern of generators/coroutines these days?
- R0b0t1 6y agoEmbedded.
- steveklabnik 6y agoYou can use async/await (and therefore generators) in Rust on embedded just fine.
- papaf 6y agoWho is writing state machines without the pattern of generators/coroutines these days? I prefer the discipline and diagrams of regular state machines. Personally, I don't like the coroutine alternative because it takes away what is good and adds complexity. Also as @R0b0t1 says, regular state machines are used in embedded: - Draw a diagram - Implement a state machine These feels much more like engineering that messing with coroutines.