Reputation: 346
So I am making a ECS based on simplecs.
I have a macro that generates a entity struct that looks like this:
($($name:ident : $component:ty,)*) => {
/// A collection of pointers to components
#[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct Entity {
$(
pub $name: Option<($component)>,
)*
children: Vec<Entity>
}
}
It is my goal to use serde to serialize the entity, but that left a bunch of ugly None values where the component should be. So I tried to implement a custom serializer that looks like this:
impl Serialize for Entity {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
let mut num_fields = 0;
$(
match self.$name {
Some => num_fields += 1,
None => {}
};
)*
let mut state = serializer.serialize_struct("Entity", num_fields)?;
// do serialize
state.end()
}
}
The serializer tries to access a field via a name supplied as a macro argument ($name
), but when I go to compile this, I get this error
error[E0530]: match bindings cannot shadow tuple variants
|
| Some => {}
| ^^^^ cannot be named the same as a tuple variant
Upvotes: 4
Views: 1911
Reputation: 523274
The syntax self.$name
is correct to access the member variable. As @oli_obk-ker said in the question's comment, the error is due to using Some
instead of Some(pattern)
.
match self.$name {
Some(_) => num_fields += 1,
// ^~~
None => {}
};
//
// even better, use `if self.$name.is_some() { num_fields += 1; }`.
However, you don't even need to write your own serialize
. You could use the #[serde(skip_serializing_if = "f")
attribute on a field, which causes the generated code avoid writing it out if f(&self.field)
returns true.
($($name:ident : $component:ty,)*) => {
/// A collection of pointers to components
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Entity {
$(
#[serde(skip_serializing_if = "Option::is_none")] // <-- add this
pub $name: Option<($component)>,
)*
children: Vec<Entity>
}
}
Upvotes: 7