Rust宏中的重复模式为何无法正常工作?——serde_yaml通用结构体反序列化宏实现求助
解决你的Serde反序列化宏重复字段报错问题
我一眼就揪出了宏代码里的两处关键问题,正是它们导致了后续字段的重复报错:
核心错误分析
- Field枚举变体匹配错误
你在match key的分支里错误地用了字段类型的驼峰命名(<$field_type:camel>),但你的Field枚举明明是基于结构体字段名生成的变体:
// 你定义的Field枚举是用字段名生成的 enum Field { $( [<$field_name:camel>] ),* } // 但匹配时却用了字段类型,完全不对应! $(Field::[<$field_type:camel>] => { /* ... */ })*
比如你的radius: f32字段,Field枚举的变体是Radius(来自字段名radius的驼峰转换),但你写的匹配分支是Field::F32,根本匹配不上,导致这个字段永远无法被正确处理,进而触发报错。
- 漏掉反序列化入口调用
你的deserialize函数最后没有调用deserializer的核心方法,相当于整个反序列化逻辑根本没有执行,这也是编译和运行报错的重要原因。
修正后的完整宏代码
我不仅修复了上述错误,还优化了细节(比如支持字段列表末尾逗号、补全完整路径):
macro_rules! impl_struct_deserialization { ( $struct_type: path { $( $field_name:ident : $field_type:path ),* $(,)? } ) => { paste! { impl<'de> serde::Deserialize<'de> for $struct_type { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { #[derive(serde::Deserialize)] #[serde(field_identifier, rename_all = "lowercase")] enum Field { $( [<$field_name:camel>] ),* } struct [<$struct_type Visitor>]; impl<'de> serde::de::Visitor<'de> for [<$struct_type Visitor>] { type Value = $struct_type; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str(&format!("struct {}", stringify!($struct_type))) } fn visit_map<V>(self, mut map: V) -> Result<$struct_type, V::Error> where V: serde::de::MapAccess<'de> { $(let mut $field_name: Option<$field_type> = None;)* while let Some(key) = map.next_key()? { match key { // 修复:用字段名的驼峰匹配Field变体 $(Field::[<$field_name:camel>] => { if $field_name.is_some() { return Err(serde::de::Error::duplicate_field(stringify!($field_name))); } $field_name = Some(map.next_value()?); })* } } $( let $field_name = $field_name.ok_or_else(|| serde::de::Error::missing_field(stringify!($field_name)))?; )* Ok($struct_type::new($($field_name),*)) } } // 新增:触发反序列化逻辑的核心调用 deserializer.deserialize_map([<$struct_type Visitor>]) } } } }; }
测试你的调用示例
现在用你提供的代码调用宏:
impl_struct_deserialization!( GBox { center: Vec3f, material: Material, radius: f32 } );
生成的Field枚举变体为Center、Material、Radius,match分支会正确匹配每个字段,字段处理逻辑正常执行,不会再出现重复字段的报错。
内容的提问来源于stack exchange,提问作者Vitalii Lukyanov
相关产品推荐
相关产品推荐

