One of the issue with type states if you aren’t aware is the state of each type needs to then be a type parameter of the parent type. Meaning creating a dynamic number of these state machines would be frustrating.<p>Do you have some thoughts or samples of say a Vec of Lights as an extension of the sample tou have?
Hey Rustaceans! I've created a library called *Statum* for building type-safe state machines in Rust. It ensures invalid state transitions are caught at *compile time*, giving you extra safety with minimal boilerplate.<p>---<p>## Quick Example (Minimal)<p>```rust
use statum::{state, machine};<p>#[state]
pub enum TaskState {
New,
InProgress,
Complete,
}<p>#[machine]
struct Task<S: TaskState> {
id: String,
name: String,
}<p>impl Task<New> {
fn start(self) -> Task<InProgress> {
self.transition()
}
}<p>impl Task<InProgress> {
fn complete(self) -> Task<Complete> {
self.transition()
}
}<p>fn main() {
let task = Task::new("task-1".to_owned(), "Important Task".to_owned())
.start()
.complete();
}
```<p>### How it works
- *`#[state]`* transforms your enum variants into separate structs while generating a trait to represent the states.
- *`#[machine]`* injects some extra fields to track which state your machine is in, at the type level.<p>---<p>## States with Data<p>Need states to carry extra information?<p>```rust
#[state]
pub enum DocumentState {
Draft,
Review(ReviewData), // State with associated data
Published,
}<p>struct ReviewData {
reviewer: String,
comments: Vec<String>,
}<p>impl Document<Draft> {
fn submit_for_review(self, reviewer: String) -> Document<Review> {
// Transition to `Review` with data:
self.transition_with(ReviewData {
reviewer,
comments: vec![],
})
}
}
```<p>### Accessing State Data<p>When your state has associated data, use `.get_state_data()` or `.get_state_data_mut()`:<p>```rust
impl Document<Review> {
fn add_comment(&mut self, comment: String) {
if let Some(review_data) = self.get_state_data_mut() {
review_data.comments.push(comment);
}
}<p><pre><code> fn get_reviewer(&self) -> Option<&str> {
self.get_state_data().map(|data| data.reviewer.as_str())
}
fn approve(self) -> Document<Published> {
self.transition()
}</code></pre>
}
```<p>---<p>## Why Statum?<p>Many Rust state machine libraries need:
- Complex trait impls
- Manual checks for transitions
- Runtime guards for state data<p>*Statum* does all of this at compile time with a straightforward API.<p>---<p>## Gotchas: Attribute Ordering
- *`#[machine]`* must come before any user `#[derive(...)]` on the same struct, or you may see:
```
error[E0063]: missing fields `marker` and `state_data`
```
This is because the derive macros expand before Statum can inject its hidden fields.<p>---<p>## Feedback Welcome!<p>I'm actively developing Statum and would love community input:
- Which patterns do you commonly use for state machines?
- Features you’d like to see added?
- Ideas to make the API smoother?<p>---<p>## Installation<p>```bash
cargo add statum
```<p>Check it out on [GitHub](<a href="https://github.com/eboody/statum">https://github.com/eboody/statum</a>) or [Crates.io](<a href="https://crates.io/crates/statum" rel="nofollow">https://crates.io/crates/statum</a>). Let me know what you think!