因此,我正在基于simplecs制作ECS 。
我有一个宏,它生成如下所示的实体结构:
($($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>
}
}
Run Code Online (Sandbox Code Playgroud)
我的目标是使用Serde序列化实体,但是在组件应放置的位置留下了一堆难看的None值。所以我尝试实现一个自定义序列化器,如下所示:
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()
}
}
Run Code Online (Sandbox Code Playgroud)
序列化程序尝试通过作为宏参数($name)提供的名称访问字段,但是当我进行编译时,出现此错误
error[E0530]: match bindings cannot shadow tuple variants
|
| Some => {}
| ^^^^ cannot be named the same as a tuple variant
Run Code Online (Sandbox Code Playgroud)
self.$name访问成员变量的语法正确。正如@ oli_obk-ker在问题评论中所说的那样,该错误是由于使用Some而不是引起的Some(pattern)。
match self.$name {
Some(_) => num_fields += 1,
// ^~~
None => {}
};
//
// even better, use `if self.$name.is_some() { num_fields += 1; }`.
Run Code Online (Sandbox Code Playgroud)
但是,您甚至不需要编写自己的serialize。您可以在#[serde(skip_serializing_if = "f")字段上使用该属性,这会导致生成的代码避免在f(&self.field)返回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>
}
}
Run Code Online (Sandbox Code Playgroud)