You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust宏中的重复模式为何无法正常工作?——serde_yaml通用结构体反序列化宏实现求助

解决你的Serde反序列化宏重复字段报错问题

我一眼就揪出了宏代码里的两处关键问题,正是它们导致了后续字段的重复报错:

核心错误分析

  1. Field枚举变体匹配错误
    你在match key的分支里错误地用了字段类型的驼峰命名(<$field_type:camel>),但你的Field枚举明明是基于结构体字段名生成的变体:
// 你定义的Field枚举是用字段名生成的
enum Field {
    $( [<$field_name:camel>] ),*
}

// 但匹配时却用了字段类型,完全不对应!
$(Field::[<$field_type:camel>] => { /* ... */ })*

比如你的radius: f32字段,Field枚举的变体是Radius(来自字段名radius的驼峰转换),但你写的匹配分支是Field::F32,根本匹配不上,导致这个字段永远无法被正确处理,进而触发报错。

  1. 漏掉反序列化入口调用
    你的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 15:17:27