stateless
A lightweight, zero-cost state machine library for Rust that separates structure from behavior.
Philosophy
This library separates state machine structure from behavior. The DSL defines valid state transitions, while your wrapper code handles guards, actions, and business logic in idiomatic Rust.
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 sequential checks
- Type safe: Leverages Rust's type system fully
- No dependencies:
no_stdcompatible - Clear code: Business logic lives in one place, not scattered
Installation
[]
= "0.1.0"
Quick Start
use statemachine;
statemachine!
Features
Guards and Actions
Guards and actions live in your wrapper code, not the DSL.
state.process_event(event) returns Option<State> - the new state if the transition is valid. You check guards, perform actions, then apply the state:
This approach gives you:
- Full control over when to apply transitions
- Multiple guards with early returns
- Actions only happen if all guards pass
- Zero coupling between state machine structure and business logic
- Clean, idiomatic Rust
State Patterns
Multiple states can share transitions:
statemachine!
Event Patterns
Multiple events can trigger the same transition:
statemachine!
Wildcard Transitions
Transition from any state:
statemachine!
Internal Transitions
Stay in the same state while performing side effects:
statemachine!
Internal transitions are useful for periodic updates, counters, or logging while remaining in the current state.
Custom Derives
statemachine!
Multiple State Machines
Use namespacing for multiple state machines:
statemachine!
statemachine!
// Generates: PlayerState, PlayerEvent with PlayerState::process_event()
// Generates: EnemyState, EnemyEvent with EnemyState::process_event()
DSL Syntax
statemachine!
Generated Code
The macro generates:
// State enum
// Event enum
// Transition method on State
Error Handling
The process_event method returns Option<State>:
Some(new_state): Transition is validNone: No valid transition for current state + event
You control when to apply the transition:
Compile Time Validation
The macro validates your state machine at compile time.
Duplicate Transitions
statemachine!
Error message:
error: duplicate transition: state 'A' + event 'Event' 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
Performance
- Zero cost: Compiles to sequential
ifchecks with early returns - No allocations: All operations are stack based
- Optimal codegen: Uses
matches!()macro for efficient pattern matching - No runtime overhead: All validation happens at compile time
FAQ
Q: How do I write guards and actions?
A: Call state.process_event(event) to get the new state if valid. Check your guards, perform actions, then apply the state. This gives you full control, zero coupling, and clean code. See the Guards and Actions section.
Q: Can I use this in no_std environments?
A: Yes! The library uses #![no_std] and only requires alloc for compilation (not at runtime).
Q: How do I handle conditional transitions?
A: state.process_event(event) returns Option<State>. Get the new state, check your guards with early returns, perform actions, then assign self.state = new_state. All guards must pass before the state changes.
Examples
See the examples directory for complete working examples:
demo.rs: Comprehensive robot control demonstrating all DSL features including guards, actions, state patterns, internal transitions, and wildcard transitionshierarchical.rs: Hierarchical state machines using composition (player movement + weapon states)
Run examples with:
License
This project is licensed under the MIT License. See the MIT.md file for details.