I would like to make a generic structure with closures, trying to model the finite state machine with Rust. A finite state machine holds two mappings(closures in this case);
- update: input X state => state
- output: input X state => output
Here is my first attempt.
pub struct Fsm<A, S, B, U, O>
where
A: Clone,
S: Clone,
B: Clone,
U: Fn(&A, &S) -> S Clone,
O: Fn(&A, &S) -> B Clone,
{
update: U,
output: O,
state: S,
_a: PhantomData<A>,
_b: PhantomData<B>,
}
impl<A, S, B, U, O> Fsm<A, S, B, U, O>
where
A: Clone,
S: Clone,
B: Clone,
U: Fn(&A, &S) -> S Clone,
O: Fn(&A, &S) -> B Clone,
{
pub fn new(update: U, output: O, state: S) -> Self {
Fsm {
update: update,
output: output,
state: state,
_a: PhantomData,
_b: PhantomData,
}
}
pub fn get_state(&self) -> S {
self.state.clone()
}
fn put_state(&mut self, state: &S) -> () {
self.state = state.to_owned();
}
pub fn react(&mut self, input: &A) -> B {
let new_state = (self.update)(input, &self.get_state());
self.put_state(&new_state);
(self.output)(input, &new_state)
}
}
impl<A, S1, B, U1, O1> Fsm<A, S1, B, U1, O1>
where
A: Clone,
S1: Clone,
B: Clone,
U1: Fn(&A, &S1) -> S1 Clone,
O1: Fn(&A, &S1) -> B Clone,
{
pub fn product<C, S2, U2, U3, O2, O3>(
self,
fsm: Fsm<B, S2, C, U2, O2>,
) -> Fsm<A, (S1, S2), C, U3, O3>
where
S2: Clone,
C: Clone,
U2: Fn(&B, &S2) -> S2 Clone,
U3: Fn(&A, &(S1, S2)) -> (S1, S2) Clone,
O2: Fn(&B, &S2) -> C Clone,
O3: Fn(&A, &(S1, S2)) -> C Clone,
{
Fsm::new(
|x, (s1, s2)| {
let new_s1 = (self.update)(x, s1);
(new_s1, (fsm.update)(&(self.output)(x, &new_s1), s2))
},
|x, (s1, s2)| {
let new_s1 = (self.update)(x, s1);
let new_s2 = (fsm.update)(&(self.output)(x, &new_s1), s2);
(fsm.output)(&(self.output)(x, &new_s1), &new_s2)
},
(self.state, fsm.state),
)
}
}
However, it has two annoying parts;
- If one does not put
_a
, and_b
compiler complains.A, B
are there to relate theU
andO
. - the product part make compile error like following;
arguments to this function are incorrect
expected type parameter `U3`
found closure `[closure@src/lib.rs:104:13: 104:26]`
every closure has a distinct type and so could not always match the caller-chosen type of parameter `U3`
expected type parameter `O3`
found closure `[closure@src/lib.rs:108:13: 108:26]`
every closure has a distinct type and so could not always match the caller-chosen type of parameter `O3`rustcClick for full compiler diagnostic
How could I resolve these issues?
CodePudding user response:
Your first concern can be easily solved by just using the constructor you defined Fsm::new
where you want to create a new instance and if you want to pattern match you can use something like Fsm{ update, ..}
as pattern and ignore everything not specified. Also with the bound O: Fn(&A, &S) -> B Clone
on the struct _b
is just not needed at all.
The first set of errors can be overcome by using existential types instead of universal generics for U3
and O3
:
pub fn product<C, S2, U2, O2>(
self,
fsm: Fsm<B, S2, C, U2, O2>,
) -> Fsm<
A,
(S1, S2),
C,
impl Fn(&A, &(S1, S2)) -> (S1, S2) Clone,
impl Fn(&A, &(S1, S2)) -> C Clone,
>
where
S2: Clone,
C: Clone,
U2: Fn(&B, &S2) -> S2 Clone,
O2: Fn(&B, &S2) -> C Clone,
{
/*...*/
}
But now you get a bunch of borrow checker errors.
You can resolve most of them by requiring 'static
closures for your types. If that's not flexible enough you'd have to replace 'static
with appropriate named lifetimes everywhere in the snippet below.
The rest can be resolved by adding clone
and move
so we create necessary copies of the Fn
arguments and move them.
impl<A, S1, B, U1, O1> Fsm<A, S1, B, U1, O1>
where
A: Clone,
S1: Clone,
B: Clone,
U1: Fn(&A, &S1) -> S1 Clone 'static,
O1: Fn(&A, &S1) -> B Clone 'static,
{
pub fn product<C, S2, U2, O2>(
self,
fsm: Fsm<B, S2, C, U2, O2>,
) -> Fsm<
A,
(S1, S2),
C,
impl Fn(&A, &(S1, S2)) -> (S1, S2) Clone,
impl Fn(&A, &(S1, S2)) -> C Clone,
>
where
S2: Clone,
C: Clone,
U2: Fn(&B, &S2) -> S2 Clone 'static,
O2: Fn(&B, &S2) -> C Clone 'static,
{
Fsm::new(
{
let self_update = self.update.clone();
let fsm_update = fsm.update.clone();
let self_output = self.output.clone();
move |x, (s1, s2)| {
let new_s1 = (self_update)(x, s1);
(new_s1.clone(), (fsm_update)(&(self_output)(x, &new_s1), s2))
}
},
move |x, (s1, s2)| {
let new_s1 = (self.update)(x, s1);
let new_s2 = (fsm.update)(&(self.output)(x, &new_s1), s2);
(fsm.output)(&(self.output)(x, &new_s1), &new_s2)
},
(self.state, fsm.state),
)
}
}