生成的 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:两者都是函数体里的项,项在整个块里可见,与书写顺序无关。
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。
问: 带 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)。
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 + content | deserialize_struct(name, &[tag, content], __Visitor) | 同上 |
untagged | ContentVisitor 作为 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字段只能整体赋值;出错时已写入的字段不回滚。