Serde 元编程

第 13 章 Deserialize 代码生成:Visitor 的自动构造

作者 杨艺韬 · 9,200 字 · 发布于 · 更新于

13.1 反序列化为什么是 Serde 最难的章节

第 12 章讲 Serialize 代码生成——结构直接:遍历字段、调 Serializer 方法、组装完事。1369 行代码看起来不少,但核心模式可以用一句话总结:"for each field, emit a serialize_field call"。

Deserialize 完全不同。看 serde_derive 的目录:

serde_derive/src/
├── ser.rs              1369 行(Serialize,单文件)
├── de.rs                976 行(Deserialize 主文件)
└── de/
    ├── struct_.rs        697 行
    ├── tuple.rs          283 行
    ├── unit.rs            52 行
    ├── enum_.rs           96 行(enum 分发)
    ├── enum_externally.rs  213 行
    ├── enum_internally.rs  106 行
    ├── enum_adjacently.rs  324 行
    ├── enum_untagged.rs   135 行
    └── identifier.rs     477 行

反序列化代码一共 3359 行,是 Serialize 的 2.5 倍。 分散在 10 个文件里,每个文件处理一种特定场景。为什么?

原因在第 4 章讲过:Deserialize 要通过 Visitor 模式反向控制。每一种类型的反序列化都要:

  1. 定义一个 Visitor(一个临时 struct)
  2. 给 Visitor 实现 Visitor trait,写 expecting + 若干 visit_* 方法
  3. 为 struct 反序列化额外实现字段名识别(一个"枚举字段"+ 它的 Visitor)
  4. 处理"可能借用输入数据"的生命周期参数 'de
  5. 处理 enum 的 4 种 tag 模式 × 4 种 variant style

每一项都需要一个独立的代码生成路径。以 13.4 节两字段的 User 为例,在 Rust 1.96.0 上用 -Zunpretty=expanded 实测,Deserialize 展开约 165 行,Serialize 只有 24 行。

本章不试图把全部 3359 行讲完——那需要一本小书。我们聚焦 3 个代表性场景:

  • 普通 struct 的 Deserialize 生成(最基础,展示 Visitor 模式如何在代码中出现)
  • 字段名识别(identifier.rs 的核心——一个独立的小 Visitor)
  • 生命周期与借用反序列化('de 如何在生成代码里出现)

第 14 章专门讲 enum 的四种 tag 策略。本章 + 第 14 章合起来覆盖了反序列化所有主要设计点。

13.2 expand_derive_deserialize 的骨架

先看入口(serde_derive/src/de.rs:25,精简):

pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result<TokenStream> {
    replace_receiver(input);

    let ctxt = Ctxt::new();
    let Some(cont) = Container::from_ast(&ctxt, input, Derive::Deserialize, &private.ident()) else {
        return Err(ctxt.check().unwrap_err());
    };
    precondition(&ctxt, &cont);
    ctxt.check()?;

    let ident = &cont.ident;
    let params = Parameters::new(&cont);
    let (de_impl_generics, _, ty_generics, where_clause) = params.generics_with_de_lifetime();
    let body = Stmts(deserialize_body(&cont, &params));
    let delife = params.borrowed.de_lifetime();   // 'de,或任一借用生命周期是 'static 时的 'static

    let impl_block = if let Some(remote) = cont.attrs.remote() {
        // remote 模式:生成一个固有方法而不是 trait impl
        let vis = &input.vis;
        quote! {
            #[automatically_derived]
            impl #de_impl_generics #ident #ty_generics #where_clause {
                #vis fn deserialize<__D>(__deserializer: __D)
                    -> _serde::#private::Result<#remote #ty_generics, __D::Error>
                where __D: _serde::Deserializer<#delife>,
                {
                    #body
                }
            }
        }
    } else {
        let fn_deserialize_in_place = deserialize_in_place_body(&cont, &params);

        // 标准 impl Deserialize
        quote! {
            #[automatically_derived]
            impl #de_impl_generics _serde::Deserialize<#delife> for #ident #ty_generics #where_clause {
                fn deserialize<__D>(__deserializer: __D)
                    -> _serde::#private::Result<Self, __D::Error>
                where __D: _serde::Deserializer<#delife>,
                {
                    #body
                }

                #fn_deserialize_in_place
            }
        }
    };

    Ok(dummy::wrap_in_const(cont.attrs.custom_serde_path(), impl_block))
}

和 Serialize 入口的相似之处:parse → Container → precondition → 主分派 → impl 外壳 → dummy 包装。

不同之处:

  1. Parameters::generics_with_de_lifetime(serde_derive/src/de.rs:174):返回四个 generics 片段,多出了一个 de_impl_generics(它的 <> 里包含 'de 生命周期)——因为 impl<'de> Deserialize<'de> for T。
  2. deserialize_in_place_body:除了 deserialize 还可能实现 deserialize_in_place——Deserialize trait 的一个进阶方法(见 13.11 节)。
  3. delife:trait 上的生命周期不是硬编码 'de——BorrowedLifetimes::de_lifetime(serde_derive/src/de.rs:263)在任一借用生命周期是 'static 时直接给 'static(serde_derive/src/de.rs:290-291 注释:此时 'de: 'static 是多余的)。

Parameters::new 里的 build_generics(serde_derive/src/de.rs:192)把 T: Deserialize<'de> bound 加进 where 子句、generics_with_de_lifetime 再补上 'de 参数,合起来生成:

// 原:struct User<T>
// 生成:impl<'de, T> Deserialize<'de> for User<T> where T: Deserialize<'de>
//           ^^^                                   ^^^^^^^^^^^^^^^^^^^^^^^^
//     de_impl_generics                                     bound

这种生命周期推导是第 15 章"借用反序列化"的前置知识。

13.3 deserialize_body:四路分派

主分派(serde_derive/src/de.rs:306):

fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
    if cont.attrs.transparent() {
        deserialize_transparent(cont, params)
    } else if let Some(type_from) = cont.attrs.type_from() {
        deserialize_from(type_from)
    } else if let Some(type_try_from) = cont.attrs.type_try_from() {
        deserialize_try_from(type_try_from)
    } else if let attr::Identifier::No = cont.attrs.identifier() {
        match &cont.data {
            Data::Enum(variants) => enum_::deserialize(params, variants, &cont.attrs),
            Data::Struct(Style::Struct, fields) => {
                struct_::deserialize(params, fields, &cont.attrs, StructForm::Struct)
            }
            Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => {
                tuple::deserialize(params, fields, &cont.attrs, TupleForm::Tuple)
            }
            Data::Struct(Style::Unit, _) => unit::deserialize(params, &cont.attrs),
        }
    } else {
        match &cont.data {
            Data::Enum(variants) => identifier::deserialize_custom(params, variants, &cont.attrs),
            Data::Struct(_, _) => unreachable!("checked in serde_derive_internals"),
        }
    }
}

四路分派——和 §12.3 一样按 match &cont.data 的臂数计:Enum 交给 enum_::deserialize,Struct / Tuple+Newtype / Unit 各交给对应子模块。注意 newtype struct 没有单独的生成函数——它和 tuple struct 合成一臂,一起走 tuple::deserialize,所以比 serialize_body 的五臂少一臂。进入这个 match 之前还有三个容器级短路:transparent / from / try_from(前两个和 Serialize 的 transparent / into 相对应);另外 #[serde(field_identifier)]、#[serde(variant_identifier)] 枚举走 else 分支交给 identifier::deserialize_custom。如果改按外层 if 链的分支数计(transparent / from / try_from / 普通类型 / identifier),则是五路。

注意 type_from 和 type_try_from——Serialize 对应的是 type_into,这是反序列化时"先反序列化为 X 类型,再 convert 成 Self" 的机制。它们常用于借一个中间类型做类型转换;try_from 还能在转换时做校验,TryFrom 返回的错误经 de::Error::custom 变成反序列化错误(serde_derive/src/de.rs:435-440)。(外部类型的问题另有 #[serde(remote = "...")] 专门处理,见第 16 章。)

13.4 deserialize_struct:Visitor 模式的落地

现在看重头戏——普通 struct 的反序列化生成。以这个用户代码为例:

#[derive(Deserialize)]
struct User {
    id: u64,
    name: String,
}

反序列化逻辑(不涉及格式细节):

  1. 告诉 Deserializer:"我期待一个 struct,名字 User,字段 [id, name]"
  2. Deserializer 解析输入,根据输入形态调用 Visitor 的 visit_seq(如果是顺序)或 visit_map(如果是键值对)
  3. Visitor 在 visit_map 里循环读取 key-value 对,识别 key 是哪个字段,把 value 存起来
  4. 所有字段读完后,组装 User 并返回

生成代码(cargo expand 观察,精简):

impl<'de> Deserialize<'de> for User {
    fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
    where __D: Deserializer<'de>,
    {
        // === 第一部分:字段标识符枚举 ===
        enum __Field {
            __field0,     // 代表 id
            __field1,     // 代表 name
            __ignore,     // 未知字段
        }

        struct __FieldVisitor;
        impl<'de> Visitor<'de> for __FieldVisitor {
            type Value = __Field;
            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                f.write_str("field identifier")
            }
            fn visit_u64<__E: de::Error>(self, v: u64) -> Result<__Field, __E> {
                match v {
                    0u64 => Ok(__Field::__field0),
                    1u64 => Ok(__Field::__field1),
                    _ => Ok(__Field::__ignore),
                }
            }
            fn visit_str<__E: de::Error>(self, v: &str) -> Result<__Field, __E> {
                match v {
                    "id" => Ok(__Field::__field0),
                    "name" => Ok(__Field::__field1),
                    _ => Ok(__Field::__ignore),
                }
            }
            fn visit_bytes<__E: de::Error>(self, v: &[u8]) -> Result<__Field, __E> {
                match v {
                    b"id" => Ok(__Field::__field0),
                    b"name" => Ok(__Field::__field1),
                    _ => Ok(__Field::__ignore),
                }
            }
        }

        impl<'de> Deserialize<'de> for __Field {
            fn deserialize<__D: Deserializer<'de>>(d: __D) -> Result<Self, __D::Error> {
                d.deserialize_identifier(__FieldVisitor)
            }
        }

        // === 第二部分:主 Visitor ===
        struct __Visitor<'de> { marker: PhantomData<User>, lifetime: PhantomData<&'de ()> }

        impl<'de> Visitor<'de> for __Visitor<'de> {
            type Value = User;

            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
                f.write_str("struct User")
            }

            // 顺序模式:从 seq 读取
            fn visit_seq<__A: SeqAccess<'de>>(self, mut __seq: __A) -> Result<User, __A::Error> {
                let __field0 = match __seq.next_element::<u64>()? {
                    Some(v) => v,
                    None => return Err(de::Error::invalid_length(0, &"struct User with 2 elements")),
                };
                let __field1 = match __seq.next_element::<String>()? {
                    Some(v) => v,
                    None => return Err(de::Error::invalid_length(1, &"struct User with 2 elements")),
                };
                Ok(User { id: __field0, name: __field1 })
            }

            // 映射模式:从 map 读取
            fn visit_map<__A: MapAccess<'de>>(self, mut __map: __A) -> Result<User, __A::Error> {
                let mut __field0: Option<u64> = None;
                let mut __field1: Option<String> = None;

                while let Some(__key) = __map.next_key::<__Field>()? {
                    match __key {
                        __Field::__field0 => {
                            if __field0.is_some() {
                                return Err(de::Error::duplicate_field("id"));
                            }
                            __field0 = Some(__map.next_value::<u64>()?);
                        }
                        __Field::__field1 => {
                            if __field1.is_some() {
                                return Err(de::Error::duplicate_field("name"));
                            }
                            __field1 = Some(__map.next_value::<String>()?);
                        }
                        _ => {   // 即 __Field::__ignore
                            __map.next_value::<de::IgnoredAny>()?;
                        }
                    }
                }

                let __field0 = __field0.ok_or_else(|| de::Error::missing_field("id"))?;   // 真实代码见 13.6 节
                let __field1 = __field1.ok_or_else(|| de::Error::missing_field("name"))?;
                Ok(User { id: __field0, name: __field1 })
            }
        }

        // === 第三部分:入口调用 ===
        const FIELDS: &[&str] = &["id", "name"];
        __deserializer.deserialize_struct("User", FIELDS, __Visitor { marker: PhantomData, lifetime: PhantomData })
    }
}

未精简的真实展开约 165 行——从 5 行用户定义生成出来。让我们拆开看它的三部分。

13.5 第一部分:字段标识符枚举 (identifier.rs)

生成代码的第一部分定义了 __Field enum 和 __FieldVisitor——它们负责识别输入里的字段名。

为什么要这个 enum? 因为 MapAccess::next_key 的返回类型是泛型 K: Deserialize<'de>。Serde 需要一个"key 类型"告诉 Deserializer"我期待一个标识符,它可能是字符串(JSON)或整数(字段序号)"。

__FieldVisitor 的三个 visit 方法:

  • visit_u64: 处理字段索引,服务于用整数序号表示字段的格式。生成代码里 0u64 → __field0、1u64 → __field1。Bincode 这类紧凑格式其实不走这里:bincode 1.3.3 的 deserialize_struct 直接转调 deserialize_tuple,走的是 visit_seq。
  • visit_str: 处理字段字符串名。JSON、YAML 等格式用字符串 key。
  • visit_bytes: 处理字段的字节字符串名。某些格式可能用字节形式传递 key(如自定义 binary 格式)。

__ignore 变体的角色:未知字段。默认行为是忽略(以支持向前兼容)。如果用户写了 #[serde(deny_unknown_fields)],这个 variant 会被替换成返回错误。

这段代码的生成逻辑在 serde_derive/src/de/identifier.rs(477 行)。它是独立文件,因为:

  1. 字段标识符的 Visitor 复杂(要处理 u64/str/bytes 三种形态)。
  2. Enum 的变体识别也走同一套逻辑——复用 identifier.rs。
  3. #[serde(alias = "...")] 支持,每个字段可以有多个别名。

13.6 第二部分:主 Visitor 和 visit_seq / visit_map

主 Visitor __Visitor 有两个 visit_* 方法:

visit_seq:处理"序列"形式输入(如果格式把 struct 编码成顺序数组)。

生成模式:

let __field0 = seq.next_element::<FIELD0_TYPE>()? 
    .ok_or_else(|| invalid_length(...))?;
let __field1 = seq.next_element::<FIELD1_TYPE>()?
    .ok_or_else(|| invalid_length(...))?;
// ...
Ok(Self { field0: __field0, field1: __field1, ... })

每个字段调一次 next_element。如果返回 None(元素不够)报 invalid_length 错。

visit_map:处理"键值映射"形式(JSON 对象的典型情况)。

生成模式:

let mut __field0: Option<T0> = None;
let mut __field1: Option<T1> = None;
// ...

while let Some(key) = map.next_key::<__Field>()? {
    match key {
        __Field::__field0 => {
            if __field0.is_some() { return Err(duplicate_field("name0")); }
            __field0 = Some(map.next_value::<T0>()?);
        }
        // ...
        _ => { map.next_value::<IgnoredAny>()?; }   // 未知字段(__ignore)
    }
}

let __field0 = __field0.ok_or_else(|| missing_field("name0"))?;
// ...
Ok(Self { ... })

几个关键设计点:

1. 为什么用 Option<T> 累积? 因为字段顺序不确定、字段可能缺失。Option::None 表示"还没收到"。最后一步的 .ok_or_else(|| missing_field("..."))? 检查必要字段是否都有。真实生成代码调的是 _serde::__private228::de::missing_field("...")?(serde/src/private/de.rs:24):它拿一个特制的 Deserializer 去反序列化字段类型,遇到 Option<T> 就返回 None、其他类型才报 missing field——所以缺了 Option 字段不会报错(serde_json 1.0.149 实测:struct Opt { id: u64, nick: Option<String> } 从 {"id":1} 反序列化出 nick: None)。

2. 重复字段检测:if __field0.is_some() { Err(duplicate_field(...)) }——JSON 规范(RFC 8259)只说 key 应当唯一、重复时接收方的行为不可预测,而 serde_derive 生成的代码明确报错(serde_json 1.0.149 实测,输入 {"id":1,"id":2} 的报错文本:duplicate field `id` at line 1 column 12)。这是 serde 的严格策略。

3. IgnoredAny:对未知字段,调用 map.next_value::<IgnoredAny>() 消耗它的 value(不管是什么格式)。IgnoredAny 是一个特殊类型,它的 Deserialize 对任何输入都成功并丢弃。

13.7 第三部分:入口调用

最后一段:

const FIELDS: &[&str] = &["id", "name"];
__deserializer.deserialize_struct("User", FIELDS, __Visitor { marker: PhantomData, lifetime: PhantomData })

告诉 Deserializer:"我期待一个名为 User 的 struct、字段列表是 FIELDS"。Deserializer 根据输入形态决定调 visit_seq 还是 visit_map。

为什么要传 FIELDS 常量? 两个用途:一是 deny_unknown_fields 时 __FieldVisitor 用它构造 Error::unknown_field(__value, FIELDS) 的错误信息;二是某些 Deserializer 实现可以用这个列表做优化(比如不写字段名的紧凑格式)。serde_json 自己并不用它——deserialize_struct 的参数名就叫 _fields(serde_json 的 src/de.rs:1823)。

PhantomData 的两个字段:

  • marker: PhantomData<User>:__Visitor 直接沿用容器的泛型参数(struct __Visitor #de_impl_generics,serde_derive/src/de/struct_.rs:167)。对 Wrapper<T> 这类泛型类型,T 必须出现在 __Visitor 的字段里,而 Rust 不允许声明了却不使用的泛型参数(E0392)——PhantomData<Wrapper<T>> 就是那次"使用",且不占空间。对 User 这种非泛型类型,它只是统一模板的产物。
  • lifetime: PhantomData<&'de ()>:同理,'de 是 __Visitor 的生命周期参数,不被使用同样报 E0392;这个 PhantomData 让它"被使用"。

13.8 de/struct_.rs:697 行的细节

上面例子是简化版。真实的生成代码在 serde_derive/src/de/struct_.rs 里生成,5 个顶层函数组织这 697 行:

行号 函数 职责
17 pub(super) fn deserialize struct 反序列化的入口——生成 impl Deserialize<'de>
199 fn deserialize_map 生成 visit_map 方法体——即 13.6 节讲的核心循环
423 pub(super) fn deserialize_in_place 生成 deserialize_in_place 方法(见 13.11 节)
513 fn deserialize_map_in_place in_place 版本的 visit_map
673 fn deserialize_field_identifier 生成 __Field + __FieldVisitor(13.5 节)

各函数长短不一,从二十几行(deserialize_field_identifier)到两百多行。deserialize_map(199-419 行)是单个最大的生成函数——它按属性(default、flatten、deny_unknown_fields、deserialize_with 等)分出近十处 if/match 分支,每种组合有对应的 quote! 模板。这里要处理的属性组合:

  • #[serde(default)] 和 #[serde(default = "fn")]:字段缺失时用默认值
  • #[serde(skip_deserializing)]:始终不反序列化此字段(用默认值)
  • #[serde(flatten)]:把字段的 struct "摊平" 到父级
  • #[serde(borrow)]:让字段借用 'de 数据(借用集合在 serde_derive/src/de.rs 的 borrowed_lifetimes 里算好,struct_.rs 只沿用算出的 delife)
  • #[serde(deserialize_with = "fn")]:自定义反序列化函数

每种属性都改变生成代码的某一部分。例如 #[serde(default = "my_fn")]:

// 原生成(无 default)
let __field0 = __field0.ok_or_else(|| missing_field("name0"))?;

// 带 default = "my_fn"(真实生成是展开的 match,见 13.13 节)
let __field0 = match __field0 { Some(v) => v, None => my_fn() };

#[serde(flatten)] 最特殊——它不能走普通字段枚举逻辑(因为 flatten 字段吸纳所有未知 key)。生成代码会完全换一种 visit_map 模式:把所有未识别的键值对收集到一个缓冲区(Vec<Option<(Content, Content)>>,serde_derive/src/de/struct_.rs:225-231),然后调用 flatten 字段的 Deserialize::deserialize 经 FlatMapDeserializer 从这个缓冲区解析。

#[serde(borrow)] 影响字段类型的生命周期约束(第 15 章详述)。

697 行的 struct_.rs 处理所有这些分支。每一行都是一个具体属性场景的代码生成逻辑。本章不展开每一条——读懂上面的基础模式,你就能在需要时按图索骥找到具体属性的处理位置。

13.9 tuple struct 和 unit struct 的反序列化

tuple struct(serde_derive/src/de/tuple.rs,283 行):

#[derive(Deserialize)]
struct Point(i32, i32);

生成的 Visitor 只实现 visit_seq——tuple struct 不走 map 路径(没有字段名)。精简后的生成代码:

impl<'de> Visitor<'de> for __Visitor<'de> {
    type Value = Point;
    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("tuple struct Point")
    }
    fn visit_seq<__A: SeqAccess<'de>>(self, mut seq: __A) -> Result<Point, __A::Error> {
        let __field0 = seq.next_element::<i32>()?
            .ok_or_else(|| invalid_length(0, &"tuple struct Point with 2 elements"))?;
        let __field1 = seq.next_element::<i32>()?
            .ok_or_else(|| invalid_length(1, &"tuple struct Point with 2 elements"))?;
        Ok(Point(__field0, __field1))
    }
}

__deserializer.deserialize_tuple_struct("Point", 2, __Visitor { ... })

简单得多——只有一个 visit 方法。

unit struct(serde_derive/src/de/unit.rs,52 行):

#[derive(Deserialize)]
struct Empty;

生成代码更简单:

struct __Visitor<'de> { marker: PhantomData<Empty>, lifetime: PhantomData<&'de ()> }
impl<'de> Visitor<'de> for __Visitor<'de> {
    type Value = Empty;
    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("unit struct Empty")
    }
    fn visit_unit<__E: de::Error>(self) -> Result<Empty, __E> {
        Ok(Empty)
    }
}

__deserializer.deserialize_unit_struct("Empty", __Visitor { marker: PhantomData, lifetime: PhantomData })

只实现 visit_unit——单元结构没有数据。unit.rs 的 52 行没有任何分支,除去 import 和准备变量,几乎全是这段 quote! 模板本身。

13.10 默认值与 skip_deserializing

#[serde(default = "my_fn")] 是最常见的"字段可以缺失"场景。

生成代码(对照看差异):

// 无 default
let __field0 = __field0.ok_or_else(|| missing_field("id"))?;

// #[serde(default)]
let __field0 = match __field0 { Some(v) => v, None => Default::default() };

// #[serde(default = "my_fn")]
let __field0 = match __field0 { Some(v) => v, None => my_fn() };

// #[serde(skip_deserializing)]
// 字段完全不参与 visit_map 的 match,直接用默认值
let __field0 = Default::default();  // 或 my_fn() 如果也有 default

默认值的处理分两阶段:

  1. 读取阶段:visit_map 里 __field0: Option<T> = None,遇到字段则 Some(...)。
  2. 组装阶段:let __field0 = match __field0 { Some(v) => v, None => my_fn() }。

#[serde(skip_deserializing)] 跳过读取阶段,直接进组装——反正从来不会被读到。

如果 整个 struct 有 #[serde(default)]:读取阶段不变(仍然是 Option<T> 累积),组装阶段多一条 let __default: Self::Value = Default::default();(serde_derive/src/de/struct_.rs:377),缺失的字段从 __default 里取(expr_is_missing 的 __default.#member 分支,serde_derive/src/de.rs:783-787):

// 读取阶段和普通 struct 一样……
let __default: User = Default::default();   // struct-level default
let __field0 = match __field0 {
    Some(v) => v,
    None => __default.id,
};
let __field1 = match __field1 {
    Some(v) => v,
    None => __default.name,
};
Ok(User { id: __field0, name: __field1 })

不会再报 missing_field 错误;代价是无论缺不缺字段都会构造一次完整的默认值。

13.11 deserialize_in_place:高级复用优化

除了标准 deserialize 方法,Serde 还提供一个可选方法 deserialize_in_place:

// serde_core/src/de/mod.rs:585
fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
where D: Deserializer<'de>,
{
    // Default implementation just delegates to `deserialize` impl.
    *place = tri!(Deserialize::deserialize(deserializer));
    Ok(())
}

默认实现 就是"调 deserialize 然后赋值给 place"。override 的价值是可以复用 place 里已有的内存——比如反序列化 JSON 数组到一个已有的 Vec<T>,可以复用 Vec 的 capacity 和部分 items,而不是重新分配。

serde_derive 在 struct 上生成这个方法:

fn deserialize_in_place<__D: Deserializer<'de>>(d: __D, place: &mut Self) -> Result<(), __D::Error> {
    // 类似 deserialize,但在 visit_seq/visit_map 里直接写到 place.field
    ...
}

默认关闭——serde_derive 默认不生成这个方法,要打开 deserialize_in_place feature;打开之后,也只有不命中 opt-out 规则的 struct 和 tuple/newtype struct 才会生成(详见 §13.11.1)。

13.11.1 deserialize_in_place 在 serde_derive 里的精确 opt-out 条件

"默认关闭"背后有两层条件——打开 serde_derive/src/de.rs:332(精简,省略参数类型和函数尾部):

#[cfg(feature = "deserialize_in_place")]                 // ← 外层 cargo feature 开关
fn deserialize_in_place_body(cont, params) -> Option<Stmts> {
    assert!(!params.has_getter);                          // ← 防御性断言

    if cont.attrs.transparent()                           // ← 六条 opt-out 之一
        || cont.attrs.type_from().is_some()               // ← opt-out 二
        || cont.attrs.type_try_from().is_some()           // ← opt-out 三
        || cont.attrs.identifier().is_some()              // ← opt-out 四
        || cont.data.all_fields()
               .all(|f| f.attrs.deserialize_with().is_some())  // ← opt-out 五
    {
        return None;
    }

    let code = match &cont.data {
        Data::Struct(Style::Struct, fields) =>
            struct_::deserialize_in_place(params, fields, &cont.attrs)?,
        Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) =>
            tuple::deserialize_in_place(params, fields, &cont.attrs),
        Data::Enum(_) | Data::Struct(Style::Unit, _) => return None,  // ← opt-out 六
    };
    // ...
}

#[cfg(not(feature = "deserialize_in_place"))]             // ← feature 不开时的 stub
fn deserialize_in_place_body(_cont, _params) -> Option<Stmts> {
    None
}

两层 gate 组合:

外层——#[cfg(feature = "deserialize_in_place")]:整个 deserialize_in_place 是 serde_derive 的可选 cargo feature,默认不开启。用户要在 Cargo.toml 里显式写:

[dependencies]
serde_derive = { version = "1", features = ["deserialize_in_place"] }

才会有任何生成代码。这不是"按类型决定"——是"整个项目决定"。可以推测的原因是:大部分 Rust 项目不需要这个优化(reuse 内存省的那点分配时间对多数应用都可以忽略),为它多生成代码会拖慢 cargo check。serde_derive 选择默认不收这个代价、想要的用户自己加 feature。

内层六条 opt-out 规则(即便 feature 开了也可能跳过):

  1. #[serde(transparent)]——透明包装只转发给内部字段、没必要额外 in-place 代码
  2. #[serde(from = "...")]——通过 From::from 转换的类型、in-place 无意义
  3. #[serde(try_from = "...")]——TryFrom 转换、同上
  4. #[serde(field_identifier)] / #[serde(variant_identifier)]——枚举被当 identifier 用、不是数据容器
  5. 所有字段都有 #[serde(deserialize_with = "...")]——用户每个字段都接管了反序列化、serde_derive 没插话空间
  6. enum 或 unit struct——枚举的结构每次变体都不同、没稳定 "place" 可更新;unit struct 没字段

此外还有一条藏在 ? 里:含 #[serde(flatten)] 字段的 struct,struct_::deserialize_in_place 直接返回 None(serde_derive/src/de/struct_.rs:430-432),经 serde_derive/src/de.rs:352 的 ? 同样放弃生成。

每条都对应一个"in-place 在这类情形下不可能工作或毫无价值"的明确论证。

assert!(!params.has_getter) 防御性断言(line 336):remote derives 用 getter 方法访问字段、不能表达 "直接写进 &mut self.field" 的 in-place 语义。前面的控制流已经保证 has_getter 不会走到这里——assert 捕获未来重构引入的 bug、在生成出错误代码之前就报错。这是 "为自己的未来重构留保险丝" 的典型手法——和《Rust 编译器与运行时揭秘》专栏第 2 章讲的 rustc debug_assert_ne! 同源。

结构体 vs 元组的分叉实现(line 350-359):

  • Style::Struct → struct_::deserialize_in_place(按名字匹配字段、需要 fallback 处理未知字段)
  • Style::Tuple / Style::Newtype → tuple::deserialize_in_place(按位置匹配、更简单)
  • Enum / Unit → None

两个分工明确的子模块——而不是一个"通用 in-place"函数。原因是 struct 版要同时生成 visit_seq 和 visit_map(外加字段识别),tuple 版只需 visit_seq——要生成的回调集合不同、代码重用没多少收益、拆两份更清晰。这和第 10 章讲的 bound.rs 用一个通用的 with_bound 函数处理所有结构形成对比——bound 分析对所有结构统一;代码生成则是结构特异的。

这一整段 50 行的 deserialize_in_place_body 是一个 "外层 feature gate + 内层语义 opt-out + 防御性 assert + 结构分叉" 的四层过滤器。如果你看到生成的代码里有 deserialize_in_place、意味着四层都通过——是个非常精挑细选的子集。

13.12 Deserialize 的 bound 推导

和 Serialize 一样,Deserialize 也要为泛型类型自动推导 bound:

#[derive(Deserialize)]
struct Wrapper<T> { data: T }

// 生成(bound 落在 where 子句里):
impl<'de, T> Deserialize<'de> for Wrapper<T> where T: Deserialize<'de> { ... }

bound.rs(425 行,第 10 章 §10.8 讲过)同时为 Serialize 和 Deserialize 工作。它遍历字段类型,对泛型参数加 T: Deserialize<'de> 约束(或 Serialize 约束)。

一个特别的 deserialize bound 问题:

struct Wrapper<T> {
    data: PhantomData<T>,  // 这个 T 并不真的参与反序列化
}

如果简单推导 T: Deserialize<'de>,用户在 PhantomData<T> 里放不能反序列化的 T 时会失败。所以 serde 特殊识别 PhantomData<T>——不为 PhantomData 里的 T 加 bound。

这个特例在 bound.rs 里只有一处硬编码:visit_path(serde_derive/src/bound.rs:131-137)看到路径最后一段是 PhantomData 就直接 return、不再往里收集类型参数。注释原文折成两行,先是 "Hardcoded exception, because PhantomData implements",下一行接 "Serialize and Deserialize whether or not T implements it."。

13.13 真实例子:完整 cargo expand 观察

把前面的内容整合,看真实的生成代码。用户:

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct UserStrict {
    id: u64,
    #[serde(default = "default_name")]
    name: String,
    #[serde(skip_deserializing)]
    internal: Vec<u8>,
}

fn default_name() -> String { "anonymous".to_string() }

cargo expand 真实输出(serde 1.0.228 + serde_derive 1.0.228,2026-04-20 实测,已精简重复结构但语义 1:1 保留):

#[automatically_derived]
impl<'de> _serde::Deserialize<'de> for UserStrict {
    fn deserialize<__D>(
        __deserializer: __D,
    ) -> _serde::__private228::Result<Self, __D::Error>
    where
        __D: _serde::Deserializer<'de>,
    {
        #[allow(non_camel_case_types)]
        #[doc(hidden)]
        enum __Field {
            __field0,
            __field1,
            // 注意:没有 __ignore——deny_unknown_fields 关闭了它
            // 注意:internal 字段(skip_deserializing)不在 __Field 中
        }

        #[doc(hidden)]
        struct __FieldVisitor;

        impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
            type Value = __Field;

            fn expecting(
                &self,
                __formatter: &mut _serde::__private228::Formatter,
            ) -> _serde::__private228::fmt::Result {
                _serde::__private228::Formatter::write_str(__formatter, "field identifier")
            }

            fn visit_u64<__E>(self, __value: u64) -> _serde::__private228::Result<Self::Value, __E>
            where __E: _serde::de::Error,
            {
                match __value {
                    0u64 => _serde::__private228::Ok(__Field::__field0),
                    1u64 => _serde::__private228::Ok(__Field::__field1),
                    _ => _serde::__private228::Err(_serde::de::Error::invalid_value(
                        _serde::de::Unexpected::Unsigned(__value),
                        &"field index 0 <= i < 2",
                    )),
                }
            }

            fn visit_str<__E>(self, __value: &str) -> _serde::__private228::Result<Self::Value, __E>
            where __E: _serde::de::Error,
            {
                match __value {
                    "id" => _serde::__private228::Ok(__Field::__field0),
                    "name" => _serde::__private228::Ok(__Field::__field1),
                    _ => _serde::__private228::Err(
                        _serde::de::Error::unknown_field(__value, FIELDS),
                    ),
                }
            }

            fn visit_bytes<__E>(self, __value: &[u8]) -> _serde::__private228::Result<Self::Value, __E>
            where __E: _serde::de::Error,
            { /* 类似 visit_str,对字节形式 key */ }
        }

        impl<'de> _serde::Deserialize<'de> for __Field {
            #[inline]
            fn deserialize<__D>(__deserializer: __D) -> _serde::__private228::Result<Self, __D::Error>
            where __D: _serde::Deserializer<'de>,
            {
                _serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor)
            }
        }

        #[doc(hidden)]
        struct __Visitor<'de> {
            marker: _serde::__private228::PhantomData<UserStrict>,
            lifetime: _serde::__private228::PhantomData<&'de ()>,
        }

        impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
            type Value = UserStrict;

            fn expecting(&self, __formatter: &mut _serde::__private228::Formatter) -> _serde::__private228::fmt::Result {
                _serde::__private228::Formatter::write_str(__formatter, "struct UserStrict")
            }

            #[inline]
            fn visit_seq<__A>(self, mut __seq: __A) -> _serde::__private228::Result<Self::Value, __A::Error>
            where __A: _serde::de::SeqAccess<'de>,
            {
                let __field0 = match _serde::de::SeqAccess::next_element::<u64>(&mut __seq)? {
                    _serde::__private228::Some(__value) => __value,
                    _serde::__private228::None => return _serde::__private228::Err(
                        _serde::de::Error::invalid_length(0usize, &"struct UserStrict with 2 elements"),
                    ),
                };
                let __field1 = match _serde::de::SeqAccess::next_element::<String>(&mut __seq)? {
                    _serde::__private228::Some(__value) => __value,
                    _serde::__private228::None => default_name(),  // default = "default_name"
                };
                let __field2 = _serde::__private228::Default::default();  // internal 字段
                _serde::__private228::Ok(UserStrict {
                    id: __field0,
                    name: __field1,
                    internal: __field2,
                })
            }

            #[inline]
            fn visit_map<__A>(self, mut __map: __A) -> _serde::__private228::Result<Self::Value, __A::Error>
            where __A: _serde::de::MapAccess<'de>,
            {
                let mut __field0: _serde::__private228::Option<u64> = _serde::__private228::None;
                let mut __field1: _serde::__private228::Option<String> = _serde::__private228::None;

                while let _serde::__private228::Some(__key) =
                    _serde::de::MapAccess::next_key::<__Field>(&mut __map)?
                {
                    match __key {
                        __Field::__field0 => {
                            if _serde::__private228::Option::is_some(&__field0) {
                                return _serde::__private228::Err(
                                    <__A::Error as _serde::de::Error>::duplicate_field("id"),
                                );
                            }
                            __field0 = _serde::__private228::Some(
                                _serde::de::MapAccess::next_value::<u64>(&mut __map)?,
                            );
                        }
                        __Field::__field1 => {
                            if _serde::__private228::Option::is_some(&__field1) {
                                return _serde::__private228::Err(
                                    <__A::Error as _serde::de::Error>::duplicate_field("name"),
                                );
                            }
                            __field1 = _serde::__private228::Some(
                                _serde::de::MapAccess::next_value::<String>(&mut __map)?,
                            );
                        }
                    }
                }

                let __field0 = match __field0 {
                    _serde::__private228::Some(__field0) => __field0,
                    _serde::__private228::None => _serde::__private228::de::missing_field("id")?,
                };
                let __field1 = match __field1 {
                    _serde::__private228::Some(__field1) => __field1,
                    _serde::__private228::None => default_name(),   // default = "default_name"
                };
                _serde::__private228::Ok(UserStrict {
                    id: __field0,
                    name: __field1,
                    internal: _serde::__private228::Default::default(),  // skip_deserializing 字段
                })
            }
        }

        #[doc(hidden)]
        const FIELDS: &'static [&'static str] = &["id", "name"];  // 不含 internal

        _serde::Deserializer::deserialize_struct(
            __deserializer, "UserStrict", FIELDS,
            __Visitor {
                marker: _serde::__private228::PhantomData::<UserStrict>,
                lifetime: _serde::__private228::PhantomData,
            },
        )
    }
}

观察点(对照前 13.4 节的基础版,精确标记三处属性的影响):

  1. deny_unknown_fields 改变了 visit_str:基础版是 _ => Ok(__Field::__ignore),这里变成 _ => Err(Error::unknown_field(__value, FIELDS))。__Field enum 也少了 __ignore 变体。注意 visit_u64 对"超出范围的 index" 返回 invalid_value(不是 unknown_field)——因为数字索引越界是"值无效",字符串未知是"字段未知",两种错误语义不同。
  2. default = "default_name":组装阶段的 match 变成 None => default_name(),替代基础版的 missing_field 错误。注意这里 不是 unwrap_or_else(default_name) 而是展开的 match 形式——更直观的错误位置管理。
  3. skip_deserializing:internal 字段在 __Field 里不存在、visit_map 里没有 __field2 局部变量、FIELDS 常量里不包含 "internal"——彻底不参与反序列化。组装时直接 _serde::__private228::Default::default() 填值。
  4. visit_seq 也处理 default:注意 visit_seq 里 __field1 为 None 时也调 default_name(),和 visit_map 一致——default 跨两种输入形态统一工作。
  5. #[inline] 属性:visit_seq、visit_map、__Field::deserialize 都带 #[inline]——serde_derive 给这些关键方法加内联提示,让 rustc 能更激进优化。

三类改动对应三个属性——每个属性只改动与自己相关的那几处。这就是第 11 章属性系统 → 第 13 章代码生成的完整流水线。

13.14 serde_derive/src/de/ 9 文件 2383 行的真实拆分

第 12 章的文件表已经点到:Deserialize 顶层入口 de.rs 只有 976 行——大头都在 serde_derive/src/de/ 子目录的 9 个文件里、总计 2383 行——

文件 行 角色
struct_.rs 697 本章 §13.4-13.8 的主角——struct + __Field + __Visitor 生成的 quote! 模板
identifier.rs 477 字段/变体标识符 visitor——跨 struct / enum 共用——为什么第 14 章的 enum 和本章 struct 都依赖它
enum_adjacently.rs 324 adjacently tagged enum({"tag": "X", "content": {...}})——最复杂的 tag 模式、独占整个文件
tuple.rs 283 tuple struct 的 visit_seq 生成
enum_externally.rs 213 externally tagged enum(默认)—— {"X": {...}}
enum_untagged.rs 135 untagged enum——按变体顺序试错回溯
enum_internally.rs 106 internally tagged({"type": "X", ...} 扁平)
enum_.rs 96 enum 的入口分派——按 cattrs.tag() 分到 4 种 tag 模式
unit.rs 52 unit struct(最简单)

加上 de.rs 本身的 976 行顶层代码、整个 Deserialize 生成器 3359 行——与 Serialize(ser.rs 单文件 1369 行)的 约 2.45 倍体量差距——根本原因是本章 §13.1 讲过的"Visitor 模式要求每种输入形态各写一个 visit_xxx 方法"。

四个 tag 模式文件的大小梯度——adjacently (324) > externally (213) > untagged (135) > internally (106)——adjacently 比最简单的 internally 复杂 3 倍——因为 adjacently 既要识别 tag 字段又要识别 content 字段、还要处理两者顺序任意(tag 先出 / content 先出)+ 嵌套泛型——工程代价最大。

一个容易被忽视的事实——identifier.rs 477 行是全 de/ 第二大——因为 struct 字段识别和 enum 变体识别本质是同一件事(都是"从输入的 string / u64 / bytes 识别成一个枚举 variant")——serde_derive 把这段逻辑抽取成单文件共用、是本目录设计纪律的体现。

13.15 缺字段、借用和 in-place:三个小分支决定生成代码的正确性

本章主要看生成代码长什么样,这里补一层源码决策:serde_derive 在生成 Deserialize 时,最容易出错的不是 visitor 外壳,而是三个边界分支。

第一,泛型 bound 必须只加给真正由 Serde 反序列化的字段。 serde_derive/src/de.rs:230-235 的注释和 needs_deserialize_bound 函数(serde_derive/src/de.rs:236-245)直接写明:带 skip_deserializing、deserialize_with、字段级 bound,或者变体级 skip_deserializing / deserialize_with / bound 的字段,不会自动生成 T: Deserialize<'de>。原因是这些字段要么根本不从输入读,要么交给用户函数读,要么用户已经给了自定义 bound。

如果宏不做这个过滤,下面这个类型会被误伤。

下面是读者示例,不是 serde_derive 源码原文——Job 和 cache 都是示例自取的名字:

#[derive(Deserialize)]
struct Job<T> {
    id: u64,
    #[serde(skip_deserializing)]
    cache: Option<T>,
}

T 没有必要实现 Deserialize<'de>,因为 cache 不从输入里来。实测(serde 1.0.228)生成的 impl 头是 impl<'de, T> _serde::Deserialize<'de> for Job<T> where T: _serde::__private228::Default:被跳过的字段要用 Default::default() 填(serde_derive/src/internals/attr.rs:1188-1193 给它补上默认值),所以宏换加的是 T: Default(serde_derive/src/de.rs:249 的 requires_default)。示例特意没用 PhantomData<T>——PhantomData 本来就被 bound.rs 豁免(13.12 节),不加 skip 也不会生成 bound,看不出这层过滤。第 10 章讲过 bound 推导是 Serde "魔法感" 的来源;这里能看到魔法的边界同样重要。

第二,借用生命周期只从参与反序列化的字段收集。 serde_derive/src/de.rs:292-303 的 borrowed_lifetimes 遍历所有字段,但在 serde_derive/src/de.rs:295-296 明确跳过 skip_deserializing 字段。这样 #[serde(borrow)] 不会因为一个被跳过字段而污染整个 impl 的 'de 约束。第 15 章会详细讲 &'de str,但在生成器里它已经体现为一个集合运算:哪些字段真的借用输入,哪些字段只是类型上带生命周期。

flowchart TD
  A[字段] --> B{skip_deserializing?}
  B -->|是| C[不加 Deserialize bound]
  B -->|是| D[不收集 borrowed lifetime]
  B -->|否| E{deserialize_with 或显式 bound?}
  E -->|是| F[尊重用户路径]
  E -->|否| G[按字段类型推导 Deserialize bound]

第三,缺字段不是统一 Default::default()。 serde_derive/src/de.rs:766-806 的 expr_is_missing 把缺字段分成四类:

情况 生成行为 源码依据
字段 #[serde(default)] 调 _serde::__private228::Default::default() serde_derive/src/de.rs:768-772
字段 #[serde(default = "path")] 调用户路径 path() serde_derive/src/de.rs:773-779
容器 #[serde(default)] 从 __default.field 取值 serde_derive/src/de.rs:783-787
没有 default 调 missing_field(name)? 或返回 missing_field 错误 serde_derive/src/de.rs:791-805

这张表解释了为什么 cargo expand 里缺字段处理看起来啰嗦。宏不能简单写 unwrap_or_default(),因为字段 default、容器 default、自定义函数、deserialize_with 的错误位置都不同。尤其 serde_derive/src/de.rs:774-777 的注释说明:如果 default = "path" 返回类型不对,错误要指向属性里的 path,而不是宏生成代码的某个临时变量。

in-place 反序列化也不是所有类型都支持。 在启用 deserialize_in_place feature 时,serde_derive/src/de.rs:332-360 会尝试生成 deserialize_in_place_body,但 serde_derive/src/de.rs:338-347 先排除 transparent、from、try_from、identifier,以及所有字段都由 deserialize_with 处理的情况;serde_derive/src/de.rs:350-359 又只允许 struct 和 tuple/newtype,enum 与 unit struct 直接返回 None。这说明 in-place 不是"把所有字段原地覆盖"这么简单,它要求生成器能明确知道每个字段如何被更新。

再把这些分支接回本章的 struct visitor:serde_derive/src/de/struct_.rs:55-65 会先把可反序列化且非 flatten 的字段收集成 FieldWithAliases;serde_derive/src/de/struct_.rs:122-130 再生成 FIELDS 常量;serde_derive/src/de/struct_.rs:139-147 对普通 struct 调 Deserializer::deserialize_struct。字段名识别则交给 serde_derive/src/de/identifier.rs:185-224,它为每个 alias 生成 string/bytes 两套路由。

因此,Deserialize 生成器不是"为每个字段写一个 match"。它要同时维护五个集合:

集合 包含什么 影响哪里
可反序列化字段 未 skip、未 flatten 的字段 __Field、FIELDS、visit_map
alias 集合 字段主名 + alias __FieldVisitor 的匹配分支
borrowed lifetime 集合 真正借用输入的字段生命周期 impl generics 的 'de: 'a
default 字段集合 字段或容器提供默认值 缺字段组装逻辑
用户接管字段 deserialize_with / 显式 bound bound 推导和错误路径

这五个集合任何一个算错,生成代码都可能出现"编译不过"或"运行时语义不对"。Serde 的成熟之处就在这里:它把这些集合在源码里明确分层,而不是把判断散落在 quote 模板字符串里。

再看 alias 的路径会更具体。serde_derive/src/de/identifier.rs:62-68 把 identifier 枚举的每个变体整理成 FieldWithAliases { ident, aliases }(struct 字段在 serde_derive/src/de/struct_.rs:55-65 做同样的事),而 serde_derive/src/de/identifier.rs:194-217 同时生成字符串匹配和字节串匹配。这样 #[serde(alias = "...")] 不需要改主 visit_map 逻辑,只是扩展字段识别器的入口集合。对 JSON 来说字段名通常是字符串;对其他格式来说,identifier 也可能以 bytes 或整数索引形态出现。Serde 把"识别字段名"单独抽出,正是为了让 struct、enum variant、alias、deny_unknown_fields 能共享一套机制。

serde_derive/src/de/struct_.rs:70-95 还展示了另一个生成策略:不是所有 struct 都生成 visit_seq。untagged struct variant 和含 flatten 的 struct 会跳过 seq 路径,因为它们必须从 map/content 形态里识别字段。serde_derive/src/de/struct_.rs:139-147 对普通 struct 调 deserialize_struct,但 serde_derive/src/de/struct_.rs:140-142 对 flatten struct 改调 deserialize_map。这说明一个 #[serde(flatten)] 不是"多收几个字段"那么简单,它会改变 Deserializer 入口,进而改变可接受的输入形态。

把这些边界放在一起,Deserialize 生成器的复杂度就不再神秘:

  1. 字段识别独立出来,解决 rename、alias、unknown field。
  2. 字段存储用 Option<T>,解决重复字段和缺字段。
  3. 组装表达式独立出来,解决 default、skip、deserialize_with。
  4. 入口分派按 struct/tuple/enum/flatten 选择,解决输入形态差异。
  5. 泛型和生命周期在外层集中计算,解决 impl 头部正确性。

这五步每一步都可以在源码里定位。读 cargo expand 时看到一大段 __FieldVisitor、__Visitor、FIELDS 常量,不要把它当宏噪音;它们分别对应这五步中的前三步。

如果你在项目里调试一个奇怪的 Deserialize 行为,可以按这个顺序缩小范围:字段名不匹配,看 __FieldVisitor;重复字段或缺字段,看 visit_map 里的 Option 存储;默认值不符合预期,看 expr_is_missing 对应的分支;flatten 行为异常,看入口是否从 deserialize_struct 变成 deserialize_map;生命周期报错,看 borrowed lifetime 集合是否被字段属性扩大。宏生成代码很长,但故障点通常落在这几个稳定位置。

13.16 本章小结

Deserialize 生成器的代码量是 Serialize 的约 2.5 倍,根本原因是 Visitor 模式的"反向控制"要求每一种类型都要定义一个临时 Visitor struct,并为它实现 Visitor trait 的若干方法。

一个 struct 的 Deserialize 生成代码三部分:

  1. 字段标识符枚举 (__Field + __FieldVisitor):识别输入中的字段 key(u64/str/bytes 三种形态)
  2. 主 Visitor (__Visitor):实现 visit_seq 和 visit_map,把字段值组装成最终 struct
  3. 入口调用:deserializer.deserialize_struct("Name", FIELDS, __Visitor { ... })

属性的影响:

  • default / default = "fn" → 组装阶段的 match 里 None => 分支改成调默认值(expr_is_missing,serde_derive/src/de.rs:766)
  • skip_deserializing → 字段不参与 __Field 和 visit_map,组装阶段用默认值
  • deny_unknown_fields → 移除 __ignore variant,unknown key 返回 error
  • alias = "..." → __FieldVisitor 的 match 里多一个分支
  • flatten → 完全改写 visit_map 逻辑(第 16 章)
  • borrow → 字段借用输入,impl 头部的 'de 多出 'de: 'a 约束(如 impl<'de: 'a, 'a>,第 15 章)

enum 的四种 tag 策略——externally / internally / adjacently / untagged——各占一个文件(4 文件合计 778 行)、入口 enum_.rs 96 行做分派——是 ch14 的主题。

动手实验

  1. cargo expand 简单 struct:写一个 2 字段 struct,cargo expand 看完整生成代码。对照本章 13.4 节。
  2. 观察 deny_unknown_fields 效果:加上属性,对比生成代码差异——__ignore 消失、visit_str 返回错误。
  3. 观察 default 属性:字段加 #[serde(default)] 或 #[serde(default = "fn")],看组装阶段的变化。
  4. 读 de/struct_.rs:697 行代码,找到第 13.4 节 "主 Visitor" 生成的对应函数。理解它如何把各种属性分支组装成最终 quote! 模板。

延伸阅读

  • serde_derive/src/de/struct_.rs 完整源码:最大的 de/ 子文件,读了这个文件你就读懂了 Deserialize 的核心。
  • Serde "Implementing Deserialize":官方手写 Deserialize 教程。看完你能理解宏生成代码为什么长这样。
  • IgnoredAny 文档:理解"消耗但丢弃"的特殊类型。
  • 《Rust 编译器与运行时揭秘》专栏第 14 章:系统讲解 Rust 宏展开算法——过程宏如何注册、TokenStream 如何输入输出、展开如何做不动点迭代——是理解 serde_derive 在编译器里是怎样被触发的前置阅读。