Skip to main content
Background Image
  1. Projects/

Mindset - Zero-Cost State Machines for Rust

Author
Glen Baker
Building open source tooling
Table of Contents

Pure guards, effectful actions, zero runtime overhead

Crates.io
Documentation
GitHub

Links: GitHub | Crates.io | Documentation

Overview
#

Mindset provides a flexible and type-safe state machine implementation that separates pure guard logic from effectful actions. Built on Stillwater’s effect system, it enables you to write state machines that are zero-cost by default, explicitly effectful when needed, and highly testable through the environment pattern.

The Problem
#

State machines are fundamental to many systems, but traditional implementations have tradeoffs:

  • Runtime overhead - Many state machine libraries impose allocations, dynamic dispatch, or complex runtime machinery
  • Hidden side effects - I/O and mutations scattered throughout transition logic make testing difficult
  • Tight coupling - Hard-coded dependencies prevent unit testing and reuse
  • Lost work - Long-running workflows lose progress when interrupted
  • All-or-nothing effects - No way to write pure state machines without carrying effect overhead

The Solution
#

Mindset provides zero-cost state machines with explicit, optional effects:

use mindset::{StateMachine, State};
use mindset::builder::{StateMachineBuilder, TransitionBuilder};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum ConnectionState {
    Disconnected,
    Connecting,
    Connected,
}

impl State for ConnectionState {}

fn main() {
    let machine = StateMachineBuilder::new()
        .initial(ConnectionState::Disconnected)
        .add_transition(
            TransitionBuilder::new()
                .from(ConnectionState::Disconnected)
                .to(ConnectionState::Connecting)
                .succeeds()
                .build()
                .unwrap()
        )
        .add_transition(
            TransitionBuilder::new()
                .from(ConnectionState::Connecting)
                .to(ConnectionState::Connected)
                .succeeds()
                .build()
                .unwrap()
        )
        .build()
        .unwrap();

    // Zero-cost transitions - compiles to direct state updates
}

Key Features
#

Pure Guards, Effectful Actions
#

Separate deterministic logic from side effects:

// Pure guard - no side effects
fn can_submit(order: &Order) -> bool {
    order.total > 0.0
}

// Effectful action - explicit environment usage
fn submit_order<Env>(order: &mut Order, env: &mut Env) -> Result<(), String>
where
    Env: PaymentProcessor + Logger,
{
    env.log(&format!("Submitting order {}", order.id));
    env.charge(order.total)?;
    Ok(())
}

Environment Pattern
#

Clean dependency injection through trait bounds:

trait PaymentProcessor {
    fn charge(&mut self, amount: f64) -> Result<(), String>;
}

trait Logger {
    fn log(&mut self, message: &str);
}

// Compose environments from multiple traits
fn process<Env>(env: &mut Env) -> Result<(), String>
where
    Env: PaymentProcessor + Logger,
{
    env.log("Processing payment");
    env.charge(99.99)?;
    Ok(())
}

Zero-Cost Abstractions
#

Pay only for what you use:

// No effects = zero overhead (compiles to direct assignment)
machine.transition(State::A, State::B);

// With effects = only pay for actual I/O
machine.transition_with_effect(State::A, State::B, |state, env| {
    env.log("Transitioning");
    env.save_to_db(state)?;
    Ok(())
});

Checkpoint and Resume
#

Built-in persistence for long-running workflows:

// Workflow automatically saves checkpoints
./workflow run --config workflow.yaml

// Resume after interruption
./workflow resume --checkpoint ./checkpoints/latest.json

// Continues from where it left off

Features:

  • Automatic checkpointing at key phases
  • JSON (human-readable) and binary (compact) formats
  • Atomic writes prevent corruption
  • Resume seamlessly after crashes or planned stops

Technical Highlights
#

  • Language: Rust
  • Foundation: Built on Stillwater’s Effect type for controlled side effects
  • Core Types: StateMachine, State, Transition, Guard, Action
  • Performance: Zero-cost pure transitions, monomorphized effect dispatch
  • Pattern: Functional core (pure guards), imperative shell (effectful actions)
  • Testing: Environment pattern enables easy mocking

Installation
#

# Add to Cargo.toml
cargo add mindset

# Or manually
[dependencies]
mindset = "0.1"

Quick Examples
#

Traffic Light (Zero Cost)
#

use mindset::state_enum;
use mindset::builder::{StateMachineBuilder, simple_transition};

state_enum! {
    enum TrafficLight {
        Red,
        Yellow,
        Green,
    }
}

let machine = StateMachineBuilder::new()
    .initial(TrafficLight::Red)
    .transitions(vec![
        simple_transition(TrafficLight::Red, TrafficLight::Green),
        simple_transition(TrafficLight::Green, TrafficLight::Yellow),
        simple_transition(TrafficLight::Yellow, TrafficLight::Red),
    ])
    .build()
    .unwrap();

Order Processing (With Effects)
#

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum OrderState {
    Draft,
    Submitted,
    Processing,
    Completed,
}

impl State for OrderState {}

struct Order {
    id: u64,
    total: f64,
}

trait PaymentProcessor {
    fn charge(&mut self, amount: f64) -> Result<(), String>;
}

fn submit_order<Env>(order: &mut Order, env: &mut Env) -> Result<(), String>
where
    Env: PaymentProcessor,
{
    env.charge(order.total)?;
    Ok(())
}

Testing with Mocks
#

struct MockEnv {
    logged: Vec<String>,
    saved: Vec<String>,
}

impl Logger for MockEnv {
    fn log(&mut self, msg: &str) {
        self.logged.push(msg.to_string());
    }
}

impl Database for MockEnv {
    fn save(&mut self, data: &str) -> Result<(), String> {
        self.saved.push(data.to_string());
        Ok(())
    }
}

#[test]
fn test_transition() {
    let mut env = MockEnv {
        logged: vec![],
        saved: vec![],
    };

    transition(&mut state, &mut env).unwrap();

    assert_eq!(env.logged.len(), 1);
    assert_eq!(env.saved.len(), 1);
}

Performance
#

Zero-Cost Pure Transitions
#

Pure transitions have zero runtime overhead:

machine.transition(State::A, State::B);

Compiles to:

state = State::B;

Effect Cost Model
#

  • No effects: Zero overhead (direct state update)
  • Single trait: Monomorphized static dispatch (zero-cost abstraction)
  • Multiple traits: One vtable lookup per trait (if using trait objects)
  • Environment mutation: Direct field access

Typical timings on modern CPU:

  • Pure transition: ~0.5ns
  • Single-effect transition: ~2-3ns
  • Multi-effect transition: ~5-10ns

Use Cases
#

Mindset is ideal for:

  • Workflow engines - Document approval, order processing, account lifecycle
  • Protocol implementations - Connection state, handshake sequences
  • Resource management - File handles, database connections, network sockets
  • Long-running processes - MapReduce workflows, batch processing with checkpoints
  • Game logic - Character states, AI behavior trees
  • Business processes - Application state, multi-step forms

Design Philosophy
#

Built on functional programming principles from Stillwater:

  1. Functional Core, Imperative Shell

    • Pure guards for business logic
    • Effectful actions at boundaries
    • Clear separation enables testing
  2. Pay Only for What You Use

    • Zero-cost by default
    • Effects are opt-in
    • No hidden overhead
  3. Explicit Over Implicit

    • Side effects visible in signatures
    • Environment dependencies via trait bounds
    • Clear data flow
  4. Testability First

    • Pure functions trivial to test
    • Mock environments for integration tests
    • Clear dependency injection

Documentation
#

Project Status
#

Active development, available on crates.io. Implements core specifications:

  • Spec 001: Core state machine with pure guards
  • Spec 002: Effect-based transitions with environment pattern
  • Spec 004: Checkpoint and resume for persistence
  • Spec 005: Builder API for ergonomic construction

Links#

Related

Stillwater - Pure Core, Imperative Shell for Rust
Postmortem - Schema Validation for Rust
Premortem - Configuration Validation for Rust
Three Patterns That Made Prodigy's Functional Migration Worth It
Debtmap - Rust Technical Debt Analyzer
Prodigy - AI Workflow Orchestration for Claude
Automating Documentation Maintenance with Prodigy: A Real-World Case Study