错误`不能与元组变体命名相同'是什么意思?

时间:2017-09-06 01:32:23

标签: macros rust

所以我正在制作一个基于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>
        }
}

我的目标是使用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()
    }
}

序列化程序尝试通过作为宏参数($name)提供的名称访问字段,但是当我去编译它时,我收到此错误

error[E0530]: match bindings cannot shadow tuple variants
  |
  |         Some => {}
  |         ^^^^ cannot be named the same as a tuple variant

1 个答案:

答案 0 :(得分:3)

语法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; }`.

但是,您甚至不需要编写自己的serialize。您可以在字段上使用#[serde(skip_serializing_if = "f") attribute,这会导致生成的代码在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>
    }
}