Do you have some thoughts or samples of say a Vec of Lights as an extension of the sample tou have?
Do you have some thoughts or samples of say a Vec of Lights as an extension of the sample tou have?
To directly manage a `Vec` of state machines, you’d need dynamic dispatch (e.g., `Box<dyn ...>`), but as you mentioned, dynamic dispatch can be a giant pain in the ass.
Instead of directly managing a `Vec` of state machines (`Vec<Light<Box<dyn ...>>>`), my approach is to work with a dynamic collection of raw data (e.g., `Vec<DbData>`) and reconstruct state machines on the fly using the `to_machine` method generated by `#[validators]`.
Why?
1. You don’t need to store state machines directly in a collection.
2. The `to_machine` method dynamically reconstructs the appropriate state machine from raw data at runtime.
3.You still retain `Statum`’s compile-time guarantees for state transitions once the machine is reconstructed.
Here’s what i mean:
use statum::{machine, state, validators};
#[state]
pub enum LightState {
Off,
On,
}
#[machine]
pub struct LightMachine<S: LightState> {
name: String,
}
impl LightMachine<Off> {
pub fn switch_on(self) -> LightMachine<On> {
self.transition()
}
}
impl LightMachine<On> {
pub fn switch_off(self) -> LightMachine<Off> {
self.transition()
}
}
// Represents dynamic data
#[derive(Clone)]
pub struct DbData {
id: String,
state: String,
}
#[validators(state = LightState, machine = LightMachine)]
impl DbData {
fn is_off(&self) -> Result<(), statum::Error> {
if self.state == "off" {
Ok(())
} else {
Err(statum::Error::InvalidState)
}
}
fn is_on(&self) -> Result<(), statum::Error> {
if self.state == "on" {
Ok(())
} else {
Err(statum::Error::InvalidState)
}
}
}
fn main() {
// raw data collection
let db_data = vec![
DbData {
id: "1".to_owned(),
state: "off".to_owned(),
},
DbData {
id: "2".to_owned(),
state: "on".to_owned(),
},
];
// do stuff with vec
// dynamically reconstruct and operate on state machines
for data in db_data {
let machine = data
.to_machine("Light".to_owned()) // Pass shared data here
.unwrap(); // Handle errors if needed
match machine {
LightMachineState::Off(light) => handle_off_light(light),
LightMachineState::On(light) => handle_on_light(light),
}
}
}
fn handle_off_light(light: LightMachine<Off>) {
println!("Turning light on: {}", light.name);
}
fn handle_on_light(light: LightMachine<On>) {
println!("Turning light offf: {}", light.name);
}
This approach is kind of indirect but it solves the underlying need: operating on many state machines dynamically without sacrificing `Statum`’s type safety.Did this address your question? If so, I’ll add it to the README—this is a great point worth documenting!