๐Ÿฆ€ Functional Rust
๐ŸŽฌ Pattern Matching Exhaustive match, destructuring, guards, let-else โ€” compiler-verified branching.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข match is exhaustive โ€” the compiler ensures every variant is handled

โ€ข Destructuring extracts fields from structs, tuples, and enums in one step

โ€ข Guards (if conditions) add extra logic to match arms

โ€ข if let and let-else provide concise single-pattern matching

โ€ข Nested patterns and @ bindings handle complex data shapes elegantly

588: Finite Automata with Match

Difficulty: 3 Level: Intermediate Model state machines with enums and match โ€” states carry data, transitions are exhaustively verified.

The Problem This Solves

State machines are everywhere: connection lifecycle, order status, UI flows, protocol parsers. Most implementations use either an integer/string state field (fragile, no compiler help) or a class hierarchy with polymorphic dispatch (lots of boilerplate, easy to forget transitions). The classic bug: you add a new state but forget to handle the transition from it in one of your event handlers. The machine silently drops events or enters an invalid state. You find out at runtime. The enum approach makes states first-class types. States can carry data (a `Running` state carries the tick count, a `Paused` state remembers where it was). The `match (state, event)` pattern is a 2D transition table. The compiler checks every combination โ€” if you add a state or event variant, every unhandled combination is a compile error.

The Intuition

A finite automaton has states, events, and a transition function: `transition(state, event) -> state`. In Rust: one enum for states, one enum for events, one function that matches on `(state, event)` pairs. The tuple match reads like a transition table. States that carry data are the key upgrade over traditional FSMs. `Running(tick_count)` isn't just a state โ€” it's a state with embedded context. `Paused(where_we_stopped)` remembers history. When you transition back to `Running` from `Paused`, you resume with the correct tick count automatically. No separate fields to keep in sync. The catch-all arm `(s, _) => s` handles all unrecognized transitions: ignore the event, stay in current state. This is a deliberate design choice made visible in the code.

How It Works in Rust

#[derive(Debug, Clone)]
enum State { Idle, Running(u32), Paused(u32), Done(u32) }

#[derive(Debug, Clone, Copy)]
enum Event { Start, Tick, Pause, Resume, Stop }

// Transition table as match on (state, event) tuple
fn transition(state: State, event: Event) -> State {
 match (state, event) {
     (State::Idle,       Event::Start)  => State::Running(0),
     (State::Running(n), Event::Tick)   => State::Running(n + 1),  // increment counter
     (State::Running(n), Event::Pause)  => State::Paused(n),       // save position
     (State::Running(n), Event::Stop)   => State::Done(n),
     (State::Paused(n),  Event::Resume) => State::Running(n),      // restore position
     (State::Paused(n),  Event::Stop)   => State::Done(n),
     (s, _)                             => s,  // ignore invalid events
 }
}

// Drive the machine through a sequence of events
let events = [Event::Start, Event::Tick, Event::Tick, Event::Pause,
           Event::Resume, Event::Tick, Event::Stop];
let mut state = State::Idle;
for event in events {
 state = transition(state, event);
 println!("{:?} -> {:?}", event, state);
}
// Output: Running(0), Running(1), Running(2), Paused(2), Running(2), Running(3), Done(3)

// Simple cyclic automaton โ€” no data needed
#[derive(Clone, Copy)]
enum Traffic { Red, Green, Yellow }

fn next_traffic(t: Traffic) -> Traffic {
 match t {
     Traffic::Red    => Traffic::Green,
     Traffic::Green  => Traffic::Yellow,
     Traffic::Yellow => Traffic::Red,
 }
}

What This Unlocks

Key Differences

ConceptOCamlRust
State type`type state = Idle \Running of int \...``enum State { Idle, Running(u32), ... }`
Transition`let transition state event = match (state, event) with``fn transition(state: State, event: Event) -> State { match (state, event) { ... } }`
Data in state`Running of int``Running(u32)`
Catch-all transition`\(s, _) -> s``(s, _) => s`
Ownership of stateGC โ€” state can be aliasedMoved in transition; no accidental sharing
#[derive(Debug,Clone)]
enum State { Idle, Running(u32), Paused(u32), Done(u32) }

#[derive(Debug,Clone,Copy)]
enum Event { Start, Tick, Pause, Resume, Stop }

fn transition(state: State, event: Event) -> State {
    match (state, event) {
        (State::Idle,       Event::Start)  => State::Running(0),
        (State::Running(n), Event::Tick)   => State::Running(n+1),
        (State::Running(n), Event::Pause)  => State::Paused(n),
        (State::Running(n), Event::Stop)   => State::Done(n),
        (State::Paused(n),  Event::Resume) => State::Running(n),
        (State::Paused(n),  Event::Stop)   => State::Done(n),
        (s,                 _)             => s,
    }
}

fn describe(s: &State) -> String {
    match s {
        State::Idle       => "idle".into(),
        State::Running(n) => format!("running (tick {})", n),
        State::Paused(n)  => format!("paused at {}", n),
        State::Done(n)    => format!("done after {} ticks", n),
    }
}

// Traffic light automaton
#[derive(Debug,Clone,Copy,PartialEq)]
enum Traffic { Red, Green, Yellow }

fn next_traffic(t: Traffic) -> Traffic {
    match t { Traffic::Red=>Traffic::Green, Traffic::Green=>Traffic::Yellow, Traffic::Yellow=>Traffic::Red }
}

fn main() {
    let events = [Event::Start,Event::Tick,Event::Tick,Event::Pause,Event::Resume,Event::Tick,Event::Stop];
    let mut s = State::Idle;
    for e in events { s = transition(s, e); println!("{:?} -> {}", e, describe(&s)); }

    let mut t = Traffic::Red;
    for _ in 0..6 { t = next_traffic(t); print!("{:?} ", t); } println!();
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn start_tick() {
        let s = transition(transition(State::Idle, Event::Start), Event::Tick);
        assert!(matches!(s, State::Running(1)));
    }
    #[test] fn pause_resume() {
        let s = transition(State::Running(5), Event::Pause);
        assert!(matches!(s, State::Paused(5)));
        let s2 = transition(s, Event::Resume);
        assert!(matches!(s2, State::Running(5)));
    }
}
(* Finite automaton in OCaml *)
type state = Idle | Running of int | Paused of int | Done of int

type event = Start | Tick | Pause | Resume | Stop

let transition state event =
  match (state, event) with
  | (Idle,       Start)  -> Running 0
  | (Running n,  Tick)   -> Running (n+1)
  | (Running n,  Pause)  -> Paused n
  | (Running n,  Stop)   -> Done n
  | (Paused n,   Resume) -> Running n
  | (Paused n,   Stop)   -> Done n
  | (s, _)               -> s

let describe = function
  | Idle      -> "idle"
  | Running n -> Printf.sprintf "running (tick %d)" n
  | Paused n  -> Printf.sprintf "paused at %d" n
  | Done n    -> Printf.sprintf "done after %d ticks" n

let () =
  let events = [Start;Tick;Tick;Pause;Resume;Tick;Stop] in
  let state = List.fold_left (fun s e ->
    let s' = transition s e in
    Printf.printf "-> %s\n" (describe s'); s'
  ) Idle events in
  ignore state