Hello all, I am seeking to have something like persistent data structures (though probably what I am trying to implement deviates from what is formally called "persistent data structures"), and I am trying to use rust's enum
system to do this.
Here is what I was thinking:
#[derive(WithState)]
// The type-state `<State>` generic isn't as important to the concept,
// but here it shows the 2 kinds of values I am working with: those that are assigned directly (like `i32`)
// and those who recursively have `EnumValue`s (see below) applied to them (like `ChildState<State>`)
struct ParentState<State> {
state: PhantomData<State>,
primitive_scalar: i32,
primitive_list: Vec<i32>,
state_scalar: ChildState<State>,
state_map: BTreeMap<String, ChildState<State>>,
}
The derive(WithState)
macro would create everything below:
Type aliases (including in this post for context)
type Parent = ParentState<Checked>;
type UncheckedParent = ParentState<Unchecked>;
EnumValue (there might be a better word for this)
- Basically field_name as PascalCase storing the value (
-At
suffixed for indexed/keyed values) - The first variant value is the type of the field, unless it has as well, in which case it is a
{struct}Value
as well. - The proc macro tries to parse the kind of value from the
syn::Type
, but they can be overridden with macro attributes.
enum ParentValue {
PrimitiveScalar(i32),
PrimitiveListAt(i32, usize),
StateScalar(ChildValue),
StateMapAt(ChildValue, String),
}
Set/Apply Change/Value
- A method to apply the value/change to the
struct
in any state, returning it in theUnchecked
state
fn apply(&mut self, value: ParentValue) -> UncheckedParent {
// ...
match {
ParentValue::PrimitiveScalar(value) => {
self.primitive_scalar = value;
},
ParentValue::PrimitiveListAt(value, key) => {
if let Some(inner_value) = self.primitive_list.get_mut(&key) {
*inner_value = value;
}
},
// and the same but calling `.apply(value)` instead of `= value`
ParentValue::StateScalar(value) => {
self.state_scalar.apply(value);
},
ParentValue::StateMapAt(value, key) => {
if let Some(inner_value) = self.state_map.get_mut(&key) {
inner_value.apply(value);
}
},
// ...
}
// ...
}
- I have been able to write proc macros to do it for
struct
s, but I'm not sure how I should approach deriving onenum
s. - I'm asking for suggestions regarding
enum
s because I can really only think of a neat/clean way to do it for anything other than variants with a single unnamed field. - I also welcome any other feedback and criticism with suggestions for improvement on any of this design.
Thanks anyone for any time you spend considering this, I appreciate it.