Rust in Everything
基于 serde 1.0.229

生成的 Deserialize

主线位置:反序列化主线里类型这一侧的全部代码。一次 from_str 里,Post::deserialize 只执行一句 deserialize_struct(.., __Visitor),之后 serde_json 回调 __Visitor::visit_map(或 visit_seq),visit_map 再一次次调 next_key::<__Field>() 和 next_value::<T>()。本页逐段看这些代码是 serde_derive/src/de.rs 和 de/ 下哪个函数生成的、每个属性改了哪一段。调用顺序的完整日志在一次往返,trait 本身在 Deserialize、Deserializer 与 Visitor 和 SeqAccess、MapAccess 与 EnumAccess。

flowchart TB
  de["Post::deserialize"] --> ds["deserialize_struct 加 FIELDS"]
  ds -->|"看到 {"| vm[visit_map]
  ds -->|"看到 ["| vs[visit_seq]
  vm --> nk["next_key 得到 __Field"]
  nk -->|字段| nv["next_value 填 Option"]
  nk -->|__ignore| ig[IgnoredAny 跳过]
  nk -->|没有键了| mf{"还有 None 吗"}
  nv --> nk
  ig --> nk
  mf -->|有| miss["missing_field 或 default"]
  mf -->|没有| ok["Ok(Post)"]
  miss --> ok
  vs --> pos["按位置 next_element"]
  pos --> ok

反序列化一侧的生成器比序列化一侧拆得细:de.rs(976 行)放入口和几个共用的片段,de/struct_.rs(697 行)生成具名字段的 struct,de/identifier.rs(477 行)生成字段和变体的标识符,de/tuple.rs、de/unit.rs 生成元组和 unit struct,enum 的四种表示各占一个 de/enum_*.rs。本页的展开都来自 serde_derive 1.0.229,只摘关键几行,// ... 标出删掉的部分;外层的 const _ 包装见 派生宏的流水线。实验在 M2 Max、macOS、rustc 1.99.0 上跑,依赖 serde = "=1.0.229"(derive)、serde_json = "=1.0.154"。

入口:deserialize_body

deserialize_body 的分派和序列化一侧对称:

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"),
        }
    }
}

多出来的一支是 #[serde(field_identifier)] / #[serde(variant_identifier)]:让用户自己的 enum 充当标识符,用的是和下面 __Field 同一个生成器。元组和 newtype 都交给 tuple::deserialize,两者的区别在里面处理。

本页大部分篇幅讲 struct_::deserialize。用的类型是:

#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct Post {
  title: String,
  tags: Vec<String>,
}

struct_::deserialize 在函数体里依次放:__Field 和它的 visitor(#field_visitor)、__Visitor 结构体和它的 Visitor 实现(expecting、visit_seq、visit_map)、常量 FIELDS,最后是唯一真正执行的一句 #dispatch:

            #[doc(hidden)]
            const FIELDS: &'static [&'static str] = &["title", "tags"];
            _serde::Deserializer::deserialize_struct(
                __deserializer,
                "Post",
                FIELDS,
                __Visitor {
                    marker: _serde::__private229::PhantomData::<Post>,
                    lifetime: _serde::__private229::PhantomData,
                },
            )

__Visitor 自己不带数据,只有两个 PhantomData:一个带着 Value 类型,一个带着 'de。生命周期参数怎么加、borrow 怎么约束 'de,在 泛型与 bound。

__Field:认出字段名

struct_.rs 末尾的 deserialize_field_identifier 先决定「认不出的键」怎么办,再调 identifier::deserialize_generated:

    let (ignore_variant, fallthrough) = if has_flatten {
        let ignore_variant = quote!(__other(_serde::#private::de::Content<'de>),);
        let fallthrough = quote!(_serde::#private::Ok(__Field::__other(__value)));
        (Some(ignore_variant), Some(fallthrough))
    } else if cattrs.deny_unknown_fields() {
        (None, None)
    } else {
        let ignore_variant = quote!(__ignore,);
        let fallthrough = quote!(_serde::#private::Ok(__Field::__ignore));
        (Some(ignore_variant), Some(fallthrough))
    };

三种情况对应 __Field 的三种形状:默认多一个 __ignore;deny_unknown_fields 什么都不加,认不出就报错;有 flatten 时多一个 __other(Content<'de>),把认不出的键原样存下来留给 flatten 字段(flatten)。Post 是第一种:

            enum __Field {
                __field0,
                __field1,
                __ignore,
            }

变体名是 __field 加字段在源码里的位置(field_i),和字段名无关,字段名只出现在下面的字符串匹配里。__FieldVisitor 实现三个 visit_*,生成器是 deserialize_identifier:

                fn visit_u64<__E>(
                    self,
                    __value: u64,
                ) -> _serde::__private229::Result<Self::Value, __E>
                where
                    __E: _serde::de::Error,
                {
                    match __value {
                        0u64 => _serde::__private229::Ok(__Field::__field0),
                        1u64 => _serde::__private229::Ok(__Field::__field1),
                        _ => _serde::__private229::Ok(__Field::__ignore),
                    }
                }
                fn visit_str<__E>(
                    self,
                    __value: &str,
                ) -> _serde::__private229::Result<Self::Value, __E>
                where
                    __E: _serde::de::Error,
                {
                    match __value {
                        "title" => _serde::__private229::Ok(__Field::__field0),
                        "tags" => _serde::__private229::Ok(__Field::__field1),
                        _ => _serde::__private229::Ok(__Field::__ignore),
                    }
                }
                fn visit_bytes<__E>(
                    self,
                    __value: &[u8],
                ) -> _serde::__private229::Result<Self::Value, __E>
                where
                    __E: _serde::de::Error,
                {
                    match __value {
                        b"title" => _serde::__private229::Ok(__Field::__field0),
                        b"tags" => _serde::__private229::Ok(__Field::__field1),
                        _ => _serde::__private229::Ok(__Field::__ignore),
                    }
                }

__Field 的 Deserialize 只有一句 deserialize_identifier(__deserializer, __FieldVisitor)。identifier 是给「字段名或变体名」用的提示:格式知道键在输入里是字符串、字节串还是整数,按实际情况回调三个方法之一。serde_json 的键只能是字符串,它的 MapKey 把 deserialize_identifier 转给 deserialize_any,回调 visit_borrowed_str 或 visit_str(一次往返的日志);visit_borrowed_str 没有生成,默认实现转给 visit_str。

visit_u64 按字段序号匹配,visit_bytes 按字节串匹配,serde_json 都用不上。要看到它们被调,换一个能给出整数键和字节串键的 Deserializer:serde::de::value::MapDeserializer 把一串键值对当成 map,键是 u64 时调 visit_u64,键是 &[u8] 时调 visit_bytes(这些小 Deserializer 在 value 模块与 IntoDeserializer)。src/bin/de_ident.rs:

use serde::de::value::{Error, MapDeserializer};
use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct P {
  a: u32,
  b: u32,
}

#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
#[allow(dead_code)]
struct Strict {
  a: u32,
  #[serde(alias = "bee")]
  b: u32,
}

fn from_pairs<'de, T, K>(pairs: Vec<(K, u32)>) -> Result<T, Error>
where
  T: Deserialize<'de>,
  K: serde::de::IntoDeserializer<'de, Error>,
{
  T::deserialize(MapDeserializer::new(pairs.into_iter()))
}

fn main() {
  // u64 keys: __FieldVisitor::visit_u64
  println!("P      u64   {:?}", from_pairs::<P, u64>(vec![(0, 1), (1, 2), (7, 9)]));
  println!("Strict u64   {:?}", from_pairs::<Strict, u64>(vec![(0, 1), (1, 2), (7, 9)]).map_err(|e| e.to_string()));
  // byte-string keys: __FieldVisitor::visit_bytes
  println!("P      bytes {:?}", from_pairs::<P, &[u8]>(vec![(b"a", 1), (b"b", 2), (b"\xffz", 9)]));
  println!("Strict bytes {:?}", from_pairs::<Strict, &[u8]>(vec![(b"a", 1), (b"b", 2), (b"\xffz", 9)]).map_err(|e| e.to_string()));
  // JSON keys are always strings: __FieldVisitor::visit_str
  println!("P      json  {:?}", serde_json::from_str::<P>(r#"{"0":1,"1":2}"#).map_err(|e| e.to_string()));
  println!("Strict json  {:?}", serde_json::from_str::<Strict>(r#"{"a":1,"bee":2}"#));
  println!("Strict json  {:?}", serde_json::from_str::<Strict>(r#"{"a":1,"z":2}"#).map_err(|e| e.to_string()));
}
P      u64   Ok(P { a: 1, b: 2 })
Strict u64   Err("invalid value: integer `7`, expected field index 0 <= i < 2")
P      bytes Ok(P { a: 1, b: 2 })
Strict bytes Err("unknown field `�z`, expected one of `a`, `b`, `bee`")
P      json  Err("missing field `a` at line 1 column 13")
Strict json  Ok(Strict { a: 1, b: 2 })
Strict json  Err("unknown field `z`, expected one of `a`, `b`, `bee` at line 1 column 10")

逐行看:

  • 整数键 0、1 认成 a、b,7 被忽略。 默认情况下 visit_u64 的兜底分支也是 __ignore。
  • deny_unknown_fields 时整数键 7 报 invalid value。 兜底分支换成 invalid_value(Unexpected::Unsigned(__value), &"field index 0 <= i < 2"),这句 expected 是生成时用字段数拼好的字符串(identifier.rs 里的 fallthrough_msg)。
  • 字节串键走 visit_bytes。 认不出的 b"\xffz" 在默认情况下忽略;deny_unknown_fields 时先用 from_utf8_lossy 转成字符串再报 unknown_field,所以错误里是替换字符 �。
  • JSON 里的 "0" 是字符串,不是序号。 它走 visit_str,匹配不到 "a",被忽略,最后缺字段。visit_u64 只对真的给出整数键的格式有用。
  • alias 只是多一个匹配分支。 "bee" 和 "b" 都映射到 __field1;FIELDS 是 &["a", "b", "bee"],别名也算在里面,所以 unknown field 的提示把三个都列出来。

deny_unknown_fields 的展开就是上面说的两处兜底,外加 __Field 里没有 __ignore:

            enum __Field {
                __field0,
                __field1,
            }
            // ...
                    match __value {
                        "a" => _serde::__private229::Ok(__Field::__field0),
                        "b" => _serde::__private229::Ok(__Field::__field1),
                        "bee" => _serde::__private229::Ok(__Field::__field1),
                        _ => {
                            _serde::__private229::Err(
                                _serde::de::Error::unknown_field(__value, FIELDS),
                            )
                        }
                    }

错误在读键的时候就报了,visit_map 里也就不需要 _ => 分支(deserialize_map 里 ignored_arm 在 deny_unknown_fields 时是 None)。__FieldVisitor 引用了后面才定义的 FIELDS:两者都是函数体里的项,项在整个块里可见,与书写顺序无关。

📝NOTE
冷知识:字段标识符从 1.0.13 起才认整数

1.0.12 生成的标识符 visitor 只有变体才有整数分支,而且是 visit_u32。#1022(首次发布在 1.0.13)里的提交「Support field ident deserialization from u32」把整数分支推广到字段,expected 文本从写死的 variant index 0 <= i < N 改成按 is_variant 选 "variant" 或 "field",就是上面输出里那句 field index 0 <= i < 2;这个 PR 关闭的 #960 要的是 CBOR 用整数代替字段名的紧凑表示。同一版本合并的 #963(「Fix identifier deserialization from non-u32」)又把 visit_u32 改成 visit_u64,起因是 #962:「some formats like CBOR store numeric values in a variable length encoding. Small variant numbers are then deserialized as an e.g. u8. Unfortunately, the generated visitor only implements visit_u32, but visit_u8 etc delegate directly to visit_u64.」Visitor 的整数默认转发链只通到 visit_u64(Deserialize、Deserializer 与 Visitor),实现在 u64 上,u8 到 u32 才都能接住。1.0.13 的发布说明写的是「Allow the tag of a field or enum variant to be deserialized from any unsigned integer type (#963)」。

visit_map:循环、重复与忽略

deserialize_map 生成的 visit_map 分三段:每个字段一个 Option 局部变量;一个 while let 循环;循环结束后把 Option 拆开。Post 的展开:

                    let mut __field0: _serde::__private229::Option<String> = _serde::__private229::None;
                    let mut __field1: _serde::__private229::Option<Vec<String>> = _serde::__private229::None;
                    while let _serde::__private229::Some(__key) = _serde::de::MapAccess::next_key::<
                        __Field,
                    >(&mut __map)? {
                        match __key {
                            __Field::__field0 => {
                                if _serde::__private229::Option::is_some(&__field0) {
                                    return _serde::__private229::Err(
                                        <__A::Error as _serde::de::Error>::duplicate_field("title"),
                                    );
                                }
                                __field0 = _serde::__private229::Some(
                                    _serde::de::MapAccess::next_value::<String>(&mut __map)?,
                                );
                            }
                            // ... __field1 同样
                            _ => {
                                let _ = _serde::de::MapAccess::next_value::<
                                    _serde::de::IgnoredAny,
                                >(&mut __map)?;
                            }
                        }
                    }
                    let __field0 = match __field0 {
                        _serde::__private229::Some(__field0) => __field0,
                        _serde::__private229::None => {
                            _serde::__private229::de::missing_field("title")?
                        }
                    };
                    // ... __field1 同样
                    _serde::__private229::Ok(Post {
                        title: __field0,
                        tags: __field1,
                    })
  • 键和值严格交替。 每一轮先 next_key::<__Field>(),按结果决定下一个 next_value 的类型。值的类型只能在知道键之后确定,这正是 MapAccess 把键和值拆成两个方法的原因。
  • Option 兼做「见过没有」的标记。 第二次见到同一个键时 is_some 为真,报 duplicate_field。重复检查放在读值之前,第二个值不会被读。
  • 未知键的值用 IgnoredAny 读掉。 serde_json 对它在字节层面整段跳过(一次往返的动脑筋)。
  • skip_deserializing 和 flatten 的字段不在这三段里。 三段的 filter 都是 !skip_deserializing() && !flatten();skip_deserializing 的字段在最后构造时直接填缺省值。

src/bin/de_struct.rs 对 Post 喂了几段不合规的输入:

{"title":"a","title":"b"}    => Err(duplicate field `title` at line 1 column 20)
{"title":"a"}                => Err(missing field `tags` at line 1 column 13)
"just a string"              => Err(invalid type: string "just a string", expected struct Post at line 1 column 15)

最后一行的 expected struct Post 来自生成的 expecting,字符串是 struct_.rs 里 format!("struct {}", params.type_name()) 拼的;serde_json 看到字符串而不是 { 或 [,用它报 invalid type。位置信息是 serde_json 补的(错误)。

缺的字段:missing_field 与 Option

循环结束还是 None 的字段,由 expr_is_missing 决定填什么:

fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment {
    match field.attrs.default() {
        attr::Default::Default => {
            let span = field.original.span();
            let func = quote_spanned!(span=> _serde::#private::Default::default);
            return quote_expr!(#func());
        }
        attr::Default::Path(path) => {
            // ...
            return Fragment::Expr(quote_spanned!(path.span()=> #path()));
        }
        attr::Default::None => { /* below */ }
    }

    match *cattrs.default() {
        attr::Default::Default | attr::Default::Path(_) => {
            let member = &field.member;
            return quote_expr!(__default.#member);
        }
        attr::Default::None => { /* below */ }
    }

    let name = field.attrs.name().deserialize_name();
    match field.attrs.deserialize_with() {
        None => {
            let span = field.original.span();
            let func = quote_spanned!(span=> _serde::#private::de::missing_field);
            quote_expr! {
                #func(#name)?
            }
        }
        Some(_) => {
            quote_expr! {
                return _serde::#private::Err(<__A::Error as _serde::de::Error>::missing_field(#name))
            }
        }
    }
}

优先级是:字段上的 default → 容器上的 default → missing_field。没有任何 default 时,普通字段调运行时函数 missing_field(name)?,带 deserialize_with 的字段直接返回 missing_field 错误。

运行时那个 missing_field 在 serde/src/private/de.rs:

/// If the missing field is of type `Option<T>` then treat is as `None`,
/// otherwise it is an error.
pub fn missing_field<'de, V, E>(field: &'static str) -> Result<V, E>
where
    V: Deserialize<'de>,
    E: Error,
{
    struct MissingFieldDeserializer<E>(&'static str, PhantomData<E>);

    // ...
    impl<'de, E> Deserializer<'de> for MissingFieldDeserializer<E>
    where
        E: Error,
    {
        type Error = E;

        fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, E>
        where
            V: Visitor<'de>,
        {
            Err(Error::missing_field(self.0))
        }

        fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, E>
        where
            V: Visitor<'de>,
        {
            visitor.visit_none()
        }

        serde_core::forward_to_deserialize_any! {
            bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
            bytes byte_buf unit unit_struct newtype_struct seq tuple
            tuple_struct map struct enum identifier ignored_any
        }
    }

    let deserializer = MissingFieldDeserializer(field, PhantomData);
    Deserialize::deserialize(deserializer)
}

它把「这个字段不存在」本身做成一份输入:一个只有 deserialize_option 会回答 visit_none、其余提示一律报 missing field 的 Deserializer,再让字段类型的 Deserialize 去读它。Option<T> 的 Deserialize 调的正是 deserialize_option(标准库类型的实现),于是得到 None;别的类型调别的提示,得到错误。derive 不需要判断字段类型是不是 Option,类型别名、Option<Option<T>> 也一样处理。错误一页讲过这个构造,下面看它的边界。src/bin/de_missing.rs:

use serde::{Deserialize, Deserializer};

#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct Plain {
  x: Option<u32>,
  y: Option<Option<u32>>,
}

#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct Value {
  v: serde_json::Value,
}

fn de_opt<'de, D: Deserializer<'de>>(d: D) -> Result<Option<u32>, D::Error> {
  Option::<u32>::deserialize(d)
}

#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct With {
  #[serde(deserialize_with = "de_opt")]
  x: Option<u32>,
}

#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct WithDefault {
  #[serde(deserialize_with = "de_opt", default)]
  x: Option<u32>,
}

fn show<T: for<'de> Deserialize<'de> + std::fmt::Debug>(ty: &str, json: &str) {
  match serde_json::from_str::<T>(json) {
    Ok(v) => println!("{ty:<12} {json:<4} => {v:?}"),
    Err(e) => println!("{ty:<12} {json:<4} => Err({e})"),
  }
}

fn main() {
  show::<Plain>("Plain", "{}");
  show::<Value>("Value", "{}");
  show::<With>("With", "{}");
  show::<WithDefault>("WithDefault", "{}");
}
Plain        {}   => Plain { x: None, y: None }
Value        {}   => Err(missing field `v` at line 1 column 2)
With         {}   => Err(missing field `x` at line 1 column 2)
WithDefault  {}   => WithDefault { x: None }
  • Option<Option<u32>> 缺了是外层 None:外层 Option 收到 visit_none 就结束了,内层没有机会读。
  • serde_json::Value 能表示 null,但缺字段时不是 Value::Null,而是报错:Value 的 Deserialize 调 deserialize_any,MissingFieldDeserializer 对它报 missing field。
  • With 的字段类型同样是 Option<u32>,de_opt 也只是调了一下 Option::deserialize,但缺字段时报错。展开里它那一段是:
                    let __field0 = match __field0 {
                        _serde::__private229::Some(__field0) => __field0,
                        _serde::__private229::None => {
                            return _serde::__private229::Err(
                                <__A::Error as _serde::de::Error>::missing_field("x"),
                            );
                        }
                    };

没有经过 MissingFieldDeserializer,de_opt 根本没被调用。加上 default 之后走第一段,填 Default::default(),即 None。

📝NOTE
没有蠢问题

问: 带 deserialize_with 的字段为什么不也调 missing_field,让 Option 照样缺省为 None?

答: 因为 missing_field::<V> 要求 V: Deserialize<'de>,它最后一行是 Deserialize::deserialize(deserializer),用的是字段类型自己的 impl。而 deserialize_with 存在的理由之一,就是字段类型可能根本没有实现 Deserialize(或者实现了但不是想要的那种)。derive 在生成代码时不知道字段类型有没有这个 impl,生成 missing_field::<那个类型> 就可能编译不过,所以对 deserialize_with 的字段只能直接报错,把选择交给用户写 default。要让缺失也经过自定义函数,得把 MissingFieldDeserializer 交给那个函数而不是交给类型;1.0.229 没有这样做,对应的 issue #2878(2025-01 提出)目前仍是 open。

visit_seq:同一个 struct 也能读数组

__Visitor 除了 visit_map 还有一个 visit_seq,由 de.rs 的 deserialize_seq 生成,按源码里的字段顺序逐个 next_element:

                fn visit_seq<__A>(
                    self,
                    mut __seq: __A,
                ) -> _serde::__private229::Result<Self::Value, __A::Error>
                where
                    __A: _serde::de::SeqAccess<'de>,
                {
                    let __field0 = match _serde::de::SeqAccess::next_element::<
                        String,
                    >(&mut __seq)? {
                        _serde::__private229::Some(__value) => __value,
                        _serde::__private229::None => {
                            return _serde::__private229::Err(
                                _serde::de::Error::invalid_length(
                                    0usize,
                                    &"struct Post with 2 elements",
                                ),
                            );
                        }
                    };
                    // ... __field1 同样,invalid_length(1usize, ..)
                    _serde::__private229::Ok(Post {
                        title: __field0,
                        tags: __field1,
                    })
                }

这是给非自描述格式准备的:bincode 1.3.3 的 deserialize_struct 把 struct 当成定长元组读,只会调 visit_seq(SeqAccess、MapAccess 与 EnumAccess有 JSON 和 bincode 的对照)。serde_json 的 deserialize_struct 两个都接:看到 { 调 visit_map,看到 [ 调 visit_seq。所以 JSON 数组也能读成 Post。用 LogDe 包一层:

fn logged(json: &str) {
  println!("-- {json}");
  let mut de = serde_json::Deserializer::from_str(json);
  match Post::deserialize(LogDe(&mut de)) {
    Ok(p) => println!("=> {p:?}"),
    Err(e) => println!("=> Err({e})"),
  }
}
-- ["serde",["rust"]]
deserialize_struct("Post", ["title", "tags"])
  visit_seq
    next_element_seed
      deserialize_string
        visit_borrowed_str("serde")
    next_element_seed
      deserialize_seq
        visit_seq
          next_element_seed
            deserialize_string
              visit_borrowed_str("rust")
          next_element_seed
            => None
=> Post { title: "serde", tags: ["rust"] }

Post 的 visit_seq 只调了两次 next_element_seed,没有第三次去确认数组结束;里面那个 Vec<String> 的 visitor 才一直取到 None。元素多了少了,两边各管一半:

["serde"]                    => Err(invalid length 1, expected struct Post with 2 elements at line 1 column 9)
["serde",[],"extra"]         => Err(trailing characters at line 1 column 13)

少了是生成代码报的 invalid_length(1, ..);多了生成代码不知道,是 serde_json 在 visit_seq 返回后调 end_seq,看到 , 后面还有值,报 TrailingCharacters。

几种情况下没有 visit_seq(struct_.rs 里那段注释写着「untagged struct variants do not get a visit_seq method. The same applies to structs that only have a map representation.」):有 flatten 的 struct 只能是 map,dispatch 也换成了 deserialize_map;untagged 的 struct 变体也不生成。skip_deserializing 的字段不占位置,visit_seq 里直接填缺省值,expected 文本里的元素个数也不算它。

default:容器与字段

default 可以写在字段上,也可以写在容器上,两者生成的代码不同。src/bin/de_default.rs:

use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(default)]
#[allow(dead_code)]
struct Config {
  port: u16,
  #[serde(default = "default_host")]
  host: String,
}

impl Default for Config {
  fn default() -> Self {
    println!("  Config::default()");
    Config { port: 80, host: "from Config::default".into() }
  }
}

fn default_host() -> String {
  println!("  default_host()");
  "from default_host".into()
}

fn main() {
  for json in [r#"{}"#, r#"{"port":8080,"host":"h"}"#, r#"[]"#, r#"[1]"#] {
    println!("{json}");
    println!("  => {:?}", serde_json::from_str::<Config>(json).unwrap());
  }
}

visit_map 循环之后的那一段展开成:

                    let __default: Self::Value = _serde::__private229::Default::default();
                    let __field0 = match __field0 {
                        _serde::__private229::Some(__field0) => __field0,
                        _serde::__private229::None => __default.port,
                    };
                    let __field1 = match __field1 {
                        _serde::__private229::Some(__field1) => __field1,
                        _serde::__private229::None => default_host(),
                    };
  • 容器上的 default 生成一行 let __default: Self::Value = Default::default();(deserialize_map 里的 let_default),缺的字段从这个整体缺省值里取 __default.port。所以容器级 default 要求整个类型实现 Default,不是每个字段。
  • 字段上的 default = "path" 优先,缺了就调 default_host();字段上写不带值的 default 则调该字段类型的 Default::default()。
  • visit_seq 里是同样的一行 let __default 加同样的取值,只是放在函数开头,元素不够时用它补,而不是报 invalid_length(expr_is_missing_seq)。
❗IMPORTANT
动脑筋:四段输入里,Config::default() 和 default_host() 各被调了几次?

平台 M2 Max、macOS、rustc 1.99.0,程序就是上面的 de_default.rs。第二段输入两个字段都给了;后两段是数组,走 visit_seq。看着展开里 let __default 那一行的位置,先数一数再看答案。

带 getter 的 remote 类型(生成的 Serialize)在这里多一步:struct_.rs 的 has_getter 分支先构造本地类型,再用 Into::<远程类型>::into 转过去,所以本地类型要能 Into 成远程类型。

元组、newtype 与 unit struct

tuple::deserialize 和 unit::deserialize 没有 __Field:元组按位置读,unit 什么都不读。

#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct Meters(f64);

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

#[derive(Deserialize, Debug)]
struct Marker;

LogDe 日志(src/bin/de_shapes.rs):

-- 1.5
deserialize_newtype_struct("Meters")
  visit_newtype_struct
    deserialize_f64
      visit_f64(1.5)
=> Meters(1.5)
-- [1,2]
deserialize_tuple_struct("Point", 2)
  visit_seq
    next_element_seed
      deserialize_i32
        visit_u64(1)
    next_element_seed
      deserialize_i32
        visit_u64(2)
=> Point(1, 2)
-- null
deserialize_unit_struct("Marker")
  visit_unit
=> Marker
  • 多字段元组调 deserialize_tuple_struct(name, 字段数),__Visitor 只有 visit_seq,和 struct 的那个是同一个生成器(deserialize_seq 的 is_struct 参数为 false)。
  • 单字段的 newtype 调 deserialize_newtype_struct,__Visitor 多一个 visit_newtype_struct(生成器是 deserialize_newtype_struct),拿到格式给的 Deserializer 直接读里面那个字段:
                fn visit_newtype_struct<__E>(
                    self,
                    __e: __E,
                ) -> _serde::__private229::Result<Self::Value, __E::Error>
                where
                    __E: _serde::Deserializer<'de>,
                {
                    let __field0: f64 = <f64 as _serde::Deserialize>::deserialize(__e)?;
                    _serde::__private229::Ok(Meters(__field0))
                }

serde_json 的 deserialize_newtype_struct 不读任何括号,直接 visit_newtype_struct(self),所以 Meters 读的是裸的 1.5。newtype 也照样生成了 visit_seq(expected 是 tuple struct Meters with 1 element),格式回调 visit_seq 时按一元组读。

  • Point 的两个 i32 收到的是 visit_u64:serde_json 把非负整数都按 u64 回调,i32 的 visitor 自己做范围检查(标准库类型的实现)。
  • unit struct 调 deserialize_unit_struct,只实现 visit_unit,serde_json 读 null。

from、try_from 与 transparent

这三个容器属性都把工作交给别的类型,生成的函数体只有一个表达式。src/bin/de_from.rs:

#[derive(Deserialize, Debug)]
#[serde(from = "u32")]
#[allow(dead_code)]
struct Celsius(f64);

impl From<u32> for Celsius {
  fn from(v: u32) -> Self {
    Celsius(v as f64)
  }
}

#[derive(Deserialize, Debug)]
#[serde(try_from = "u32")]
#[allow(dead_code)]
struct Even(u32);

impl TryFrom<u32> for Even {
  type Error = String;
  fn try_from(v: u32) -> Result<Self, String> {
    if v % 2 == 0 { Ok(Even(v)) } else { Err(format!("{v} is odd")) }
  }
}

#[derive(Deserialize, Debug)]
#[serde(transparent)]
#[allow(dead_code)]
struct Id<T> {
  raw: u64,
  marker: PhantomData<T>,
}

fn main() {
  println!("{:?}", serde_json::from_str::<Celsius>("21"));
  println!("{:?}", serde_json::from_str::<Even>("4"));
  println!("{:?}", serde_json::from_str::<Even>("3").map_err(|e| e.to_string()));
  println!("{:?}", serde_json::from_str::<Even>(r#""4""#).map_err(|e| e.to_string()));
  println!("{:?}", serde_json::from_str::<Id<String>>("7"));
}
Ok(Celsius(21.0))
Ok(Even(4))
Err("3 is odd")
Err("invalid type: string \"4\", expected u32 at line 1 column 3")
Ok(Id { raw: 7, marker: PhantomData<alloc::string::String> })

三段展开:

            // from = "u32"
            _serde::__private229::Result::map(
                <u32 as _serde::Deserialize>::deserialize(__deserializer),
                _serde::__private229::From::from,
            )
            // try_from = "u32"
            _serde::__private229::Result::and_then(
                <u32 as _serde::Deserialize>::deserialize(__deserializer),
                |v| {
                    _serde::__private229::TryFrom::try_from(v)
                        .map_err(_serde::de::Error::custom)
                },
            )
            // transparent
            _serde::__private229::Result::map(
                _serde::Deserialize::deserialize(__deserializer),
                |__transparent| Id {
                    raw: __transparent,
                    marker: _serde::__private229::PhantomData,
                },
            )
  • deserialize_from 和 deserialize_try_from 先按中间类型读,再转换。输入的形状完全由中间类型决定:"4" 是字符串,u32 不收,报的是 expected u32,Even 的名字不会出现在错误里。
  • try_from 的转换错误经 de::Error::custom 变成格式的错误,所以要求 TryFrom::Error: Display。"3 is odd" 后面没有行列号:serde_json 只给在某个 deserialize_* 内部产生的错误补位置,而这个错误是 u32::deserialize 返回之后才在 and_then 里造出来的,已经出了 serde_json 的代码(错误讲 fix_position)。
  • deserialize_transparent 读唯一的数据字段。哪个字段是「数据字段」由 check.rs 的 check_transparent 挑:类型路径最后一段是 PhantomData 的、skip_deserializing 的、带 default 的都不算,剩下的必须恰好一个。其余字段按 default 属性填,没有 default 的写成 PhantomData。和 newtype struct 不同,它不调 deserialize_newtype_struct,格式看不到外层。

enum:四种表示各调什么

enum 的 Deserialize 交给 de/enum_.rs,按标签方式分给四个文件,各自的最外层调用是:

表示生成代码的第一个调用讲它的页面
外部标签(默认)deserialize_enum(name, VARIANTS, __Visitor)外部标签
tag = "..."deserialize_any(TaggedContentVisitor),先缓冲成 Content内部标签与相邻标签
tag + contentdeserialize_struct(name, &[tag, content], __Visitor)同上
untaggedContentVisitor 作为 seed 读出 Content,再逐个变体试untagged

变体名的识别用的仍是本页的 deserialize_identifier,is_variant 为真:__Field 换成变体,兜底分支换成 unknown_variant(__value, VARIANTS),整数分支的 expected 是 variant index 0 <= i < N。1.0.229 的 #2936(去掉生成代码里的 ?)改写的三处里有一处就是外部标签的 visit_enum,见怎么读这一卷。

deserialize_in_place 的生成

deserialize_in_place 是什么、复用缓冲的效果,在 Deserialize、Deserializer 与 Visitor 里已经用实验证明过。这里只看打开 serde_derive 的 deserialize_in_place feature 之后,derive 多生成了什么。deserialize_in_place_body 先排除不支持的情况(transparent、from、try_from、标识符、所有字段都 deserialize_with、enum、unit struct),具名字段的 struct 交给 struct_::deserialize_in_place,它在有 flatten 时也返回 None。

feature 要单独放在一个 crate 里打开(cargo 会把 feature 合并到整个依赖图)。exp-serde-gen-inplace 的依赖是 serde_derive = { version = "=1.0.229", features = ["deserialize_in_place"] },类型是:

fn de_upper<'de, D: Deserializer<'de>>(d: D) -> Result<String, D::Error> {
  String::deserialize(d).map(|s| s.to_uppercase())
}

#[derive(Deserialize, Debug)]
struct Post {
  title: String,
  #[serde(deserialize_with = "de_upper")]
  lang: String,
}

展开出来的 impl 里多了一个 fn deserialize_in_place(__deserializer, __place: &mut Self),它有自己的一套 __Field 和 __Visitor。__Visitor 不再是两个 PhantomData,而是持有目标:

            struct __Visitor<'de, 'place> {
                place: &'place mut Post,
                lifetime: _serde::__private229::PhantomData<&'de ()>,
            }
            #[automatically_derived]
            impl<'de, 'place> _serde::de::Visitor<'de> for __Visitor<'de, 'place> {
                type Value = ();
                // ...

Value = ():结果直接写进 place,不返回值。visit_map 由 deserialize_map_in_place 生成,局部变量从 Option<T> 换成了 bool,因为值已经写进去了,只需要记「见过没有」:

                    let mut __field0: bool = false;
                    let mut __field1: bool = false;
                    while let _serde::__private229::Some(__key) = _serde::de::MapAccess::next_key::<
                        __Field,
                    >(&mut __map)? {
                        match __key {
                            __Field::__field0 => {
                                if __field0 {
                                    return _serde::__private229::Err(
                                        <__A::Error as _serde::de::Error>::duplicate_field("title"),
                                    );
                                }
                                _serde::de::MapAccess::next_value_seed(
                                    &mut __map,
                                    _serde::__private229::de::InPlaceSeed(&mut self.place.title),
                                )?;
                                __field0 = true;
                            }
                            __Field::__field1 => {
                                // ... 重复检查
                                {
                                    // ... 局部的 __DeserializeWith 包装类型
                                    self.place.lang = match _serde::de::MapAccess::next_value::<
                                        __DeserializeWith<'de>,
                                    >(&mut __map) {
                                        _serde::__private229::Ok(__wrapper) => __wrapper.value,
                                        _serde::__private229::Err(__err) => {
                                            return _serde::__private229::Err(__err);
                                        }
                                    };
                                };
                                __field1 = true;
                            }
                            // ... _ => IgnoredAny
                        }
                    }
                    if !__field0 {
                        self.place.title = _serde::__private229::de::missing_field(
                            "title",
                        )?;
                    }
                    if !__field1 {
                        return _serde::__private229::Err(
                            <__A::Error as _serde::de::Error>::missing_field("lang"),
                        );
                    }
                    _serde::__private229::Ok(())
  • 普通字段经 InPlaceSeed(&mut self.place.title) 递归调 String::deserialize_in_place,旧缓冲得以复用。
  • deserialize_with 的字段没法原地读:用户函数只会返回一个新值,所以生成代码读出新值再整个赋给 self.place.lang。
  • 缺字段时的规则和普通版本相同,只是改成「赋值」或「直接返回错误」两种(check_flags 那段按 deserialize_with 且无 default 区分)。

visit_seq 用的是 deserialize_seq_in_place,普通字段是 if let None = next_element_seed(&mut __seq, InPlaceSeed(..))? { 报 invalid_length },deserialize_with 字段是一个三分支的 match:

                        match _serde::de::SeqAccess::next_element::<
                            __DeserializeWith<'de>,
                        >(&mut __seq) {
                            _serde::__private229::Ok(
                                _serde::__private229::Some(__wrap),
                            ) => {
                                self.place.lang = __wrap.value;
                            }
                            _serde::__private229::Ok(_serde::__private229::None) => {
                                return _serde::__private229::Err(
                                    _serde::de::Error::invalid_length(
                                        1usize,
                                        &"struct Post with 2 elements",
                                    ),
                                );
                            }
                            _serde::__private229::Err(__err) => {
                                return _serde::__private229::Err(__err);
                            }
                        }

这就是 #2936 在 1.0.229 改写过的三处之一:原来是 match next_element(..)? { Some.. None.. }。

在同一个 Post 上连续原地读三次(src/bin/inplace_gen.rs,每次打印结果、当前值和 title 的缓冲地址有没有变):

{"title":"a","lang":"en"}    Ok(()) Post { title: "a", lang: "EN" } title reused: true
["b","zh"]                   Ok(()) Post { title: "b", lang: "ZH" } title reused: true
{"title":"c"}                Err("missing field `lang` at line 1 column 13") Post { title: "c", lang: "ZH" } title reused: true

第三次报了错,但 title 已经被改成 "c",lang 还是上一次的 "ZH"。这就是 deserialize_in_place 文档里说的,出错后 place 的状态「indeterminate」:字段是边读边写的,错误发生前写进去的不会回滚。

动脑筋答案

{}
  Config::default()
  default_host()
  => Config { port: 80, host: "from default_host" }
{"port":8080,"host":"h"}
  Config::default()
  => Config { port: 8080, host: "h" }
[]
  Config::default()
  default_host()
  => Config { port: 80, host: "from default_host" }
[1]
  Config::default()
  default_host()
  => Config { port: 1, host: "from default_host" }

Config::default() 每段都调了一次,共 4 次;default_host() 调了 3 次。

  • let __default = Default::default(); 在 visit_map 里放在循环之后、取值之前,在 visit_seq 里放在函数开头,两处都是无条件执行的。第二段两个字段都给了,__default 一个字段都没用上,Config::default() 照样被调用。Default 实现很贵(或者有副作用)时要注意这一点。
  • default_host() 只在 host 缺失时调:它是 match 的 None 分支里的表达式,Some 时不求值。
  • 后两段走 visit_seq:[] 两个元素都缺,[1] 缺第二个,缺的位置用缺省值补,而不是报 invalid length。port 缺失时取的是 __default.port,host 缺失时字段自己的 default 优先,所以永远不会是 "from Config::default"。

小结

  • derive 为 struct 在 deserialize 里定义 __Field、__FieldVisitor、__Visitor 和 FIELDS,只执行一句 deserialize_struct。__FieldVisitor 用 visit_str、visit_bytes、visit_u64 三种方式认字段名,经 deserialize_identifier 由格式选;整数分支从 1.0.13 起才有,并且实现在 u64 上(#1022、#963)。
  • visit_map 每个字段一个 Option,while let 取键,第二次见到同一个键报 duplicate_field,认不出的键用 IgnoredAny 跳过;deny_unknown_fields 去掉 __ignore,在读键时就报 unknown_field,FIELDS 里包括别名。
  • 缺的字段按「字段 default → 容器 default → missing_field」处理。missing_field 让字段类型去读一个只认 deserialize_option 的 MissingFieldDeserializer,Option 因此得到 None;deserialize_with 的字段不经过它,缺了直接报错,要自己加 default。
  • visit_seq 按位置读,所以 JSON 数组也能读成 struct;少元素是生成代码报 invalid_length,多元素是 serde_json 的 end_seq 报 trailing characters。有 flatten 的 struct 和 untagged 的 struct 变体没有 visit_seq。
  • 容器级 default 每次反序列化都无条件调一次 Default::default()。from / try_from / transparent 都是一个 Result::map / and_then 表达式,try_from 的错误经 custom 转换,serde_json 不给它补位置。
  • deserialize_in_place 的 __Visitor 持有 &'place mut T,Value = (),用 bool 记字段、InPlaceSeed 递归;deserialize_with 字段只能整体赋值;出错时已写入的字段不回滚。

💬 关联讨论

发起讨论
加载中...