Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Typestate: State Machines That Compile

How do we prevent a method from being called in the wrong state? Take the enum idea one step further: give each state its own type, and make transitions consume the old state.

pub struct Unarmed(SafetyMonitor);
pub struct Armed(SafetyMonitor);

impl Unarmed {
    pub fn arm(self, checks: PreflightReport) -> Result<Armed, ArmError> {
        todo!()  // consumes self; the Unarmed value is gone on success
    }
}

impl Armed {
    pub fn trigger(&mut self) -> Action { todo!() }
    pub fn disarm(self) -> Unarmed { todo!() }
}

There’s no way to call trigger on an unarmed monitor. The method doesn’t exist on that type, so the mistake is caught while the code is being generated rather than when it runs.

When to use it:

Use typestate whenUse a plain enum when
Wrong-state calls are costly (safety, money, data loss)States change often at runtime and are inspected together
The state graph is small and stableThe graph is large or data-driven
Compile-time proof is worth some API weightMixed states must live in one collection

Builders with required fields are a common everyday form of this pattern: build() only exists once the required fields are set.