alun
October 7, 2020, 8:04pm
1
Hello! I'm trying to make a generic serialization/deserialization of a state transition logic. The code looking something like below:
pub trait StateTransition<STATE>
where
STATE: Serialize + DeserializeOwned,
{
fn act(&self, state: STATE) -> Result<STATE>;
}
fn act_generic<STATE>(state: String, actor: Box<dyn StateTransition<STATE>>) -> Result<String>
where
STATE: Serialize + DeserializeOwned,
{
let state: STATE = serde_json::from_str(&state)?;
let new_state: STATE = actor.act(state)?;
Ok(serde_json::to_string(&new_state)?)
}
struct StateTransition1 {};
struct StateTransition2 {};
#[derive(Serialize, Deserialize)]
struct State1 {
x: u32,
};
#[derive(Serialize, Deserialize)]
struct State2 {
y: u32,
};
impl StateTransition<State1> for StateTransition1 {
fn act(&self, state: State1) -> Result<State1> {
Ok(State1 { x: state.x + 1 })
}
}
impl StateTransition<State2> for StateTransition2 {
fn act(&self, state: State2) -> Result<State2> {
Ok(State2 { y: state.y + 2 })
}
}
println!(
"{:?}",
act_generic(
r#"{ "x": 1, "y": 2}"#.to_string(),
Box::new(StateTransition1 {})
)
);
println!(
"{:?}",
act_generic(
r#"{ "x": 1, "y": 2}"#.to_string(),
Box::new(StateTransition2 {})
)
);
This works nicely. But the question is how could I return dyn StateTransition<???>
from a factory function. Or which is quite the same save multiple dyn StateTransition
instances to a vector. So I could act_generic
in a loop E.g.
let transitions: Vec<Box<dyn StateTransition<???>>> =
vec![Box::new(StateTransition1 {}), Box::new(StateTransition2 {})];
As a first step to someone answering this question, I made your example code readable and compilable (i.e. remove incorrect semicolons and add imports and an fn main
, apply rustfmt
, add syntax highlighting)
use serde::{Serialize, Deserialize, de::DeserializeOwned};
use serde_json::Result;
pub trait StateTransition<STATE>
where
STATE: Serialize + DeserializeOwned,
{
fn act(&self, state: STATE) -> Result<STATE>;
}
fn act_generic<STATE>(state: String, actor: Box<dyn StateTransition<STATE>>) -> Result<String>
where
STATE: Serialize + DeserializeOwned,
{
let state: STATE = serde_json::from_str(&state)?;
let new_state: STATE = actor.act(state)?;
Ok(serde_json::to_string(&new_state)?)
}
struct StateTransition1 {}
struct StateTransition2 {}
#[derive(Serialize, Deserialize)]
struct State1 {
x: u32,
}
#[derive(Serialize, Deserialize)]
struct State2 {
y: u32,
}
impl StateTransition<State1> for StateTransition1 {
fn act(&self, state: State1) -> Result<State1> {
Ok(State1 { x: state.x + 1 })
}
}
impl StateTransition<State2> for StateTransition2 {
fn act(&self, state: State2) -> Result<State2> {
Ok(State2 { y: state.y + 2 })
}
}
fn main() {
println!(
"{:?}",
act_generic(
r#"{ "x": 1, "y": 2}"#.to_string(),
Box::new(StateTransition1 {})
)
);
println!(
"{:?}",
act_generic(
r#"{ "x": 1, "y": 2}"#.to_string(),
Box::new(StateTransition2 {})
)
);
}
(playground )
Feel free to read the pinned post about syntax highlighting .
2 Likes
alun
October 8, 2020, 8:43am
3
Thanks Steffahn! I've copied from a test method body hence the semicolons. But playground links looks much nicer.
alun
October 12, 2020, 9:06am
4
After thinking of it for a while I've found a solution which leverages:
Associated types
Blanket trait implementation
Trait objects
Overall associated trait types were the key to find a solution.
Here is working code
pub trait StateTransition {
type STATE: Serialize + DeserializeOwned;
fn act(&self, state: Self::STATE) -> Result<Self::STATE>;
}
pub trait GenericStateTransition {
fn act_generic(&self, state: String) -> Result<String>;
}
impl<T: StateTransition> GenericStateTransition for T {
fn act_generic(&self, state: String) -> Result<String> {
let state: T::STATE = serde_json::from_str(&state)?;
let new_state = self.act(state)?;
Ok(serde_json::to_string(&new_state)?)
}
}
struct StateTransition1 {}
struct StateTransition2 {}
#[derive(Serialize, Deserialize)]
struct State1 {
x: u32,
}
#[derive(Serialize, Deserialize)]
struct State2 {
y: u32,
}
impl StateTransition for StateTransition1 {
type STATE = State1;
fn act(&self, state: State1) -> Result<State1> {
Ok(State1 { x: state.x + 1 })
}
}
impl StateTransition for StateTransition2 {
type STATE = State2;
fn act(&self, state: State2) -> Result<State2> {
Ok(State2 { y: state.y + 2 })
}
}
fn main() {
let actors: Vec<Box<dyn GenericStateTransition>> =
vec![Box::new(StateTransition1 {}), Box::new(StateTransition2 {})];
for actor in actors {
println!(
"{:?}",
actor.act_generic(r#"{ "x": 1, "y": 2}"#.to_string(),)
);
}
}
system
Closed
January 10, 2021, 9:06am
5
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.