stateless
A lightweight, zero-cost state machine library for Rust that separates structure from behavior.
Philosophy
Most state machine libraries couple behavior to the state machine itself — guards, actions, and context structs all tangled into the DSL. This makes the state machine hard to test, hard to refactor, and impossible to compose.
stateless takes the opposite approach: the macro is a pure transition table. It generates two enums and a lookup function. Guards, side effects, and error handling live in your own code, using normal Rust patterns. The state machine doesn't know your types exist, and your types don't depend on any framework trait.
Why Use This?
- Zero coupling: State machine knows nothing about your types
- Idiomatic Rust: Use
Result, methods, and proper error handling - Zero cost: Compiles to efficient
matches!()checks with early returns - Type safe: Leverages Rust's type system fully
- No runtime dependencies: Generated code uses only
core— no allocator needed,no_stdcompatible - Clear code: Business logic lives in one place, not scattered
Installation
[]
= "0.4.0"
Quick Start
Define your state machine with the DSL, then use process_event to drive transitions:
use statemachine;
statemachine!
let mut state = default; // Idle (marked with *)
assert_eq!;
if let Some = state.process_event
assert_eq!;
process_event returns Option<State> — Some(new_state) if the transition is valid, None if not. This lets you insert guards and actions between checking validity and applying the transition.
Generated Code
Given this DSL:
statemachine!
The macro generates:
Features
Guards and Actions
Guards and actions live in your wrapper code, not the DSL. Call process_event to check validity, verify your guards, perform side effects, then apply the state:
Initial State
Mark the initial state with *. This state is used for the generated Default implementation:
statemachine!
let state = default; // State::Idle
State Patterns
Multiple source states can share a transition:
statemachine!
Event Patterns
Multiple events can trigger the same transition:
statemachine!
Wildcard Transitions
Transition from any state. Specific transitions always take priority over wildcards, regardless of declaration order:
statemachine!
Internal Transitions
Stay in the current state while performing side effects:
statemachine!
Custom Derives
Default derives are Debug, Copy, Clone, PartialEq, Eq. Override with derive_states and derive_events:
statemachine!
Multiple State Machines
Use name for namespacing when you need multiple state machines in the same scope:
statemachine!
statemachine!
// Generates: PlayerState, PlayerEvent, EnemyState, EnemyEvent
Compile Time Validation
The macro validates your state machine at compile time:
- Duplicate transitions — same state + event pair defined twice
- Multiple initial states — more than one state marked with
* - Empty transitions — no transitions defined
- Duplicate wildcards — same event used in multiple wildcard transitions
statemachine!
error: duplicate transition: state 'A' + event 'Go' is already defined
help: each combination of source state and event can only appear once
note: if you need conditional behavior, use different events or handle logic in your wrapper
DSL Reference
statemachine!
FAQ
Q: Can I use this in no_std environments?
A: Yes. The generated code uses only core types (Option, Default) and requires no allocator at runtime.
Examples
See the examples directory for complete working examples:
demo.rs: Robot control demonstrating guards, actions, state patterns, internal transitions, and wildcardshierarchical.rs: Hierarchical state machines using composition (player movement + weapon states)
License
This project is licensed under the MIT License. See the MIT.md file for details.