Reputation: 1735
Suppose we define a generic struct, with many fields, representing a type-safe state machine using a phantom type:
struct Foo<State> {
a: A,
b: B,
c: C,
//...
state: PhantomData<State>,
}
We can then write a type-safe state transition:
impl Foo<SourceState> {
fn transition(self, extra: X) -> Foo<DestinationState> {
let Foo {a, b, c, state: _} = self;
// do lots of stuff
Foo { a, b, c, state: PhantomData }
}
}
But we need to awkwardly unpack every field and re-pack in in a different structure.
We could also use mem::transmute
, although my understanding is that different monomorphizations of the same struct are not guaranteed to have the same memory layout.
I hoped that Foo { state: PhantomData, ..self }
would work; alas, it fails to compile.
Is there any canonical, ergonomic, safe way to write this ?
Upvotes: 2
Views: 599
Reputation: 42759
There is no way to do that in a straight forward manner, because they are 2 different types: that's the whole point of your code, actually. To simplify it, I'd do that in 2 steps, with a generic transition being the 2nd one:
use core::marker::PhantomData;
struct Foo<State> {
a: i32,
b: i32,
c: i32,
//...
state: PhantomData<State>,
}
struct SourceState;
struct DestinationState;
impl<Src> Foo<Src> {
fn transition<Dest>(self) -> Foo<Dest> {
let Foo {a, b, c, state: _} = self;
Foo { a, b, c, state: PhantomData }
}
}
impl Foo<SourceState> {
fn to_destination_state(mut self, extra: ()) -> Foo<DestinationState> {
// Do whatever you want with self
self.transition()
}
}
Alternatively, you can abstract the fact that you have a state:
mod stateful {
use core::marker::PhantomData;
pub struct Stateful<T, State> {
pub data: T,
state: PhantomData<State>,
}
impl<T, SrcState> Stateful<T, SrcState> {
pub fn transform<DestState>(self) -> Stateful<T, DestState> {
let Stateful { data, state: _ } = self;
Stateful {
data,
state: Default::default(),
}
}
}
}
struct Data {
a: i32,
b: i32,
c: i32,
}
struct SourceState;
struct DestinationState;
type Foo<State> = stateful::Stateful<Data, State>;
impl Foo<SourceState> {
fn to_destination_state(mut self, extra: ()) -> Foo<DestinationState> {
// Do whatever you want with self.data
self.transform()
}
}
Upvotes: 2