Skip to content

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

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

每一项都需要一个独立的代码生成路径。单个 struct 的 Deserialize 生成代码可能有 80-200 行(Serialize 通常 30-50 行)。

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

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

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

13.2 expand_derive_deserialize 的骨架

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

rust
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, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(&params);
    let body = Stmts(deserialize_body(&cont, &params));

    let in_place_body = deserialize_in_place_body(&cont, &params);
    let in_place_impl_generics = ...;

    let impl_block = if let Some(remote) = cont.attrs.remote() {
        // remote 模式
        quote! {
            impl #de_impl_generics #ident #ty_generics #where_clause {
                pub fn deserialize<__D>(__deserializer: __D)
                    -> _serde::#private::Result<#remote #ty_generics, __D::Error>
                where __D: _serde::Deserializer<'de>,
                {
                    #body
                }
            }
        }
    } else {
        // 标准 impl Deserialize
        quote! {
            #[automatically_derived]
            impl #de_impl_generics _serde::Deserialize<'de> for #ident #ty_generics #where_clause {
                fn deserialize<__D>(__deserializer: __D)
                    -> _serde::#private::Result<Self, __D::Error>
                where __D: _serde::Deserializer<'de>,
                {
                    #body
                }

                #in_place_body
            }
        }
    };

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

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

不同之处

  1. split_with_de_lifetime:返回四个 generics 片段,多出了一个 de_impl_generics(它的 <> 里包含 'de 生命周期)——因为 impl<'de> Deserialize<'de> for T
  2. deserialize_in_place_body:除了 deserialize 还可能实现 deserialize_in_place——Deserialize trait 的一个进阶方法(第 4 章的 deserialize_in_place 文档说过)。

split_with_de_lifetime 是一个值得记住的函数。它在原 generics 上加 'deT: Deserialize<'de> bound,生成:

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

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

13.3 deserialize_body:五路分派

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

rust
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 {
        match &cont.data {
            Data::Enum(variants) => deserialize_enum(params, variants, &cont.attrs),
            Data::Struct(Style::Struct, fields) => deserialize_struct(params, fields, &cont.attrs, StructForm::Struct),
            Data::Struct(Style::Tuple, fields) => deserialize_tuple(params, fields, &cont.attrs, TupleForm::Tuple),
            Data::Struct(Style::Newtype, fields) => deserialize_newtype_struct(params, &fields[0], &cont.attrs),
            Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs),
        }
    }
}

和 Serialize 对称的五路分派:transparent / from / try_from / struct 四种 style / enum。

注意 type_fromtype_try_from——Serialize 对应的是 type_into,这是反序列化时"先反序列化为 X 类型,再 convert 成 Self" 的机制。它们是 remote 类型常用的 workaround 之一。

13.4 deserialize_struct:Visitor 模式的落地

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

rust
#[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 观察,精简):

rust
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"))?;
                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 })
    }
}

这是一个 80+ 行的生成代码——从 10 行用户定义生成出来。让我们拆开看它的三部分。

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

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

为什么要这个 enum? 因为 Deserializer 对 next_key 的返回类型是泛型 T: Deserialize。Serde 需要一个"key 类型"告诉 Deserializer"我期待一个标识符,它可能是字符串(JSON)或整数(Bincode 里字段按序号编码)"。

__FieldVisitor 的三个 visit 方法

  • visit_u64: 处理字段索引。Bincode 等紧凑格式不写字段名,按顺序给出字段索引。生成代码里 0u64 → __field01u64 → __field1
  • 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 编码成顺序数组)。

生成模式:

rust
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 对象的典型情况)。

生成模式:

rust
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>()?);
        }
        // ...
        __Field::__ignore => { map.next_value::<IgnoredAny>()?; }
    }
}

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

几个关键设计点

1. 为什么用 Option<T> 累积? 因为字段顺序不确定、字段可能缺失。Option::None 表示"还没收到"。最后一步的 .ok_or_else(|| missing_field("..."))? 检查必要字段是否都有。

2. 重复字段检测if __field0.is_some() { Err(duplicate_field(...)) }——JSON 规范说"重复 key 行为未定义",但 serde 明确报错。这是 serde 的严格策略。

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

13.7 第三部分:入口调用

最后一段:

rust
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 常量? 因为某些 Deserializer 实现可以用这个列表做优化——比如 JSON deserializer 可以在解析 key 时用 FIELDS 做快速对比,而不是每次都构造 String。

PhantomData 的两个字段

  • marker: PhantomData<User>:让编译器把 __Visitor<'de>User 类型关联起来——需要时推导出"Visitor 的 Value 是 User"。
  • lifetime: PhantomData<&'de ()>:让 'de 生命周期在 __Visitor 里 "有意义"——即使 Visitor 内部当前没有借用 'de 数据,这个 PhantomData 声明了"如果将来有借用字段,生命周期是 'de"。

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

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

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

每个函数约 100-300 行。deserialize_map(199-421 行)是单个最大的生成函数——它按属性(defaultflattendeny_unknown_fieldsborrow 等)分成十几个 if/match 分支,每种组合有对应的 quote! 模板。这里要处理的属性组合:

  • #[serde(default)]#[serde(default = "fn")]:字段缺失时用默认值
  • #[serde(skip_deserializing)]:始终不反序列化此字段(用默认值)
  • #[serde(flatten)]:把字段的 struct "摊平" 到父级
  • #[serde(borrow)]:让字段借用 'de 数据
  • #[serde(deserialize_with = "fn")]:自定义反序列化函数

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

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

// 带 default = "my_fn"
let __field0 = __field0.unwrap_or_else(my_fn);

#[serde(flatten)] 最特殊——它不能走普通字段枚举逻辑(因为 flatten 字段吸纳所有未知 key)。生成代码会完全换一种 visit_map 模式:收集所有未知字段到一个 Map,然后调用 flatten 字段的 Deserialize::deserialize 从这个 Map 解析。

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

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

13.9 tuple struct 和 unit struct 的反序列化

tuple structde/tuple.rs,283 行):

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

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

rust
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 structde/unit.rs,52 行):

rust
#[derive(Deserialize)]
struct Empty;

生成代码更简单:

rust
struct __Visitor;
impl<'de> Visitor<'de> for __Visitor {
    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)

只实现 visit_unit——单元结构没有数据。52 行代码是因为要处理一些边缘情况,不是核心逻辑复杂。

13.10 默认值与 skip_deserializing

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

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

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

// #[serde(default)]
let __field0 = __field0.unwrap_or_else(Default::default);

// #[serde(default = "my_fn")]
let __field0 = __field0.unwrap_or_else(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,直接初始化成默认值:

rust
let mut __result = User::default();  // struct-level default
while let Some(key) = __map.next_key::<__Field>()? {
    match key {
        __Field::__field0 => { __result.id = __map.next_value()?; }
        __Field::__field1 => { __result.name = __map.next_value()?; }
        __Field::__ignore => { __map.next_value::<IgnoredAny>()?; }
    }
}
Ok(__result)

更紧凑、性能更好(不用 Option wrap)、也不会报 missing_field 错误。

13.11 deserialize_in_place:高级复用优化

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

rust
// serde_core/src/de/mod.rs:585
fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
where D: Deserializer<'de>,
{
    *place = Deserialize::deserialize(deserializer)?;
    Ok(())
}

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

serde_derive 在 struct 上生成这个方法

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

默认关闭——只在类型有优化空间时生成。因为大多数类型(原子类型、简单 struct)默认实现就够了。

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

"默认关闭"这句话掩盖了两层真相——打开 serde_derive/src/de.rs:332

rust
#[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_placeserde_derive可选 cargo feature默认不开启。用户要在 Cargo.toml 里显式写:

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

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

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

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

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

assert!(!params.has_getter) 防御性断言(line 336):remote derives 用 getter 方法访问字段、不能表达 "直接写进 &mut self.field" 的 in-place 语义。前面的控制流已经保证 has_getter 不会走到这里——assert 捕获未来重构时的 rustc bug、在 serde_derive 自己崩之前就报错。这是 "为自己的未来重构留保险丝" 的典型手法——和第 2 章 rustc debug_assert_ne! 同源。

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

  • Style::Structstruct_::deserialize_in_place(按名字匹配字段、需要 fallback 处理未知字段)
  • Style::Tuple / Style::Newtypetuple::deserialize_in_place(按位置匹配、更简单)
  • Enum / UnitNone

两个分工明确的子模块——而不是一个"通用 in-place"函数。原因是 struct 的 field lookup 用 visit_map、tuple 的用 visit_seq——Visitor 回调签名完全不同、代码重用没多少收益、拆两份更清晰。这和第 10 章讲的 bound.rs 只有一个通用函数形成对比——bound 分析对所有结构统一;代码生成则是结构特异的。

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

13.12 Deserialize 的 bound 推导

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

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

// 生成:
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Wrapper<T> { ... }

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

一个特别的 deserialize bound 问题

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

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

这类特殊情况在 bound.rs 里有完整清单。你读这个文件会看到许多 if let Type::Path(path) = &field.ty { if segment.ident == "PhantomData" ...——每一处都是一个 serde 学过的教训。

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

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

rust
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct User {
    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 保留):

rust
#[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_deserializinginternal 字段在 __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_seqvisit_map__Field::deserialize 都带 #[inline]——serde_derive 给这些关键方法加内联提示,让 rustc 能更激进优化。

三个小改动对应三个属性——每个属性精确修改生成代码的一处。这就是第 11 章属性系统 → 第 13 章代码生成的完整流水线。

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

ch12 里只提到 Deserialize 顶层入口在 de.rs(976 行)——实际上大头都在 serde_derive/src/de/ 子目录的 9 个文件里、总计 2383 行——

文件角色
struct_.rs697本章 §13.4-13.8 的主角——structuret + __Field + __Visitor 生成的近千行 quote! 模板
identifier.rs477字段/变体标识符 visitor——跨 struct / enum 共用——为什么 §14 enum 和本章 struct 都依赖它
enum_adjacently.rs324adjacently tagged enum({"tag": "X", "content": {...}})——最复杂的 tag 模式、独占整个文件
tuple.rs283tuple struct 的 visit_seq 生成
enum_externally.rs213externally tagged enum(默认)—— {"X": {...}}
enum_untagged.rs135untagged enum——按变体顺序试错回溯
enum_internally.rs106internally tagged({"type": "X", ...} 扁平)
enum_.rs96enum 的入口分派——按 cattrs.tag() 分到 4 种 tag 模式
unit.rs52unit struct(最简单)

加上 de.rs 本身的 976 行顶层代码、整个 Deserialize 生成器 3359 行——与 Serialize(ser.rs 单文件 1369 行)的 3.06 倍体量差距——根本原因是本章 §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 把这段逻辑抽取成单文件共用、是本目录设计纪律的体现。

跨书索引——mcp/chapters/03-jsonrpc-message.md 用的所有 #[derive(Deserialize)] 都是本章讲的这个生成器产出——读过本章再看那里的代码能指出每一处为什么是那样。

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

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

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

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

rust
#[derive(Deserialize)]
struct Job<T> {
    id: u64,
    #[serde(skip_deserializing)]
    marker: std::marker::PhantomData<T>,
}

T 没有必要实现 Deserialize<'de>,因为 marker 不从输入里来。第 10 章讲过 bound 推导是 Serde "魔法感" 的来源;这里能看到魔法的边界同样重要。

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

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

情况生成行为源码依据
字段 #[serde(default)]_serde::__private::Default::default()de.rs:764-769
字段 #[serde(default = "path")]调用户路径 path()de.rs:770-775
容器 #[serde(default)]__default.field 取值de.rs:780-784
没有 defaultmissing_field(name)? 或返回 missing_field 错误de.rs:788-800

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

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

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

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

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

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

再看 alias 的路径会更具体。serde_derive/src/de/identifier.rs:62-68 把每个 variant 或字段整理成 FieldWithAliases { ident, aliases },而 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 形态里识别字段。struct_.rs:139-147 对普通 struct 调 deserialize_struct,但 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__VisitorFIELDS 常量,不要把它当宏噪音;它们分别对应这五步中的前三步。真正的工程理解,是能从展开代码反推回生成器为什么要有这些集合和分支。

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

这套排查顺序也能帮助你写自定义反序列化。不要一开始就手写整个 Visitor,先把字段识别、临时存储、缺省策略和最终组装分开。Serde 生成器之所以长,是因为它把这些责任拆开后逐一处理;手写代码也应遵守同样边界。

这也是本章和第 4 章的连接点:Visitor 模式给了反向控制的接口,代码生成器则把接口所需的临时类型和临时状态全部补齐。没有生成器,用户要亲手维护这些状态。

因此,展开代码越长,越要先找状态变量和错误分支,而不是从第一行顺读到最后一行。

13.15 本章小结

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

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

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

属性的影响

  • default / default = "fn" → 组装阶段 unwrap_or_else
  • skip_deserializing → 字段不参与 __Field 和 visit_map,组装阶段用默认值
  • deny_unknown_fields → 移除 __ignore variant,unknown key 返回 error
  • alias = "..." → __FieldVisitor 的 match 里多一个分支
  • flatten → 完全改写 visit_map 逻辑(第 16 章)
  • borrow → 字段类型从 Option<String> 变成 Option<&'de str>(第 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 在编译器里是怎样被触发的前置阅读。

基于 VitePress 构建