SeqAccess、MapAccess 与 EnumAccess
主线位置:反序列化主线的中段。serde_json 看到
{,调__Visitor::visit_map(MapAccess);__Visitor用next_key::<__Field>()取出字段名,再按字段用next_value::<T>()取值,每一次取都递归地回到上一页的deserialize_*→visit_*。这一页讲格式交出来的这几个 Access trait,和它们的参数DeserializeSeed。
sequenceDiagram participant D as json 解析器 participant V as Post 的 Visitor participant M as json MapAccess participant K as 字段名 seed participant S as String 的 seed D->>V: visit_map(M) V->>M: next_key_seed(K) M->>K: deserialize(MapKey) K-->>V: Some(field0) V->>M: next_value_seed(S) M->>S: deserialize(解析器) S-->>V: hi V->>M: next_key_seed(K) M-->>V: None V-->>D: Post
visit_seq、visit_map、visit_enum 拿到的参数不是一整个已经解析好的序列,而是一个由格式实现的对象,Visitor 每要一个元素就问它一次。格式不用先把整个数组读进内存,Visitor 也可以在中途决定每个元素按什么类型读。四个 trait 都在 serde_core/src/de/mod.rs 的后半部分,加起来不到 600 行,大半是文档。
| trait | 由谁交给谁 | 必需方法 |
|---|---|---|
SeqAccess<'de> | 格式 → visit_seq | next_element_seed |
MapAccess<'de> | 格式 → visit_map | next_key_seed、next_value_seed |
EnumAccess<'de> | 格式 → visit_enum | variant_seed,外加关联类型 Variant |
VariantAccess<'de> | variant_seed 的返回值之一 | unit_variant、newtype_variant_seed、tuple_variant、struct_variant |
serde 0.9 里这四个 trait 叫 SeqVisitor、MapVisitor、EnumVisitor、VariantVisitor。issue #873 提议改名:「I dislike that Visitor and SeqVisitor are both "visitors" but one of them is the responsibility of the data structure and the other is the responsibility of the data format.」后面还有一句「The SeqVisitor etc have never been visitors but are providing access to the data contained inside a seq/map/enum/variant.」PR #876「Access traits」改成现在的名字,随 1.0.0 发布。发布说明里的说法是:「The old names conflicted with the Visitor trait which plays a totally different role, and this was a source of confusion for new and even experienced Serde users.」
改名之后分工可以按名字记:数据类型提供 Deserialize 和 Visitor,数据格式提供 Deserializer 和各个 ...Access。上表第二列就是这条线。
DeserializeSeed:先看参数
每个 Access 的必需方法都以 _seed 结尾,参数是一个 DeserializeSeed:
pub trait DeserializeSeed<'de>: Sized {
/// The type produced by using this seed.
type Value;
/// Equivalent to the more common `Deserialize::deserialize` method, except
/// with some initial piece of data (the seed) passed in.
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>;
}
和 Deserialize 比只差一处:deserialize 有 self。Deserialize::deserialize 是关联函数,除了类型参数,什么信息都带不进去;seed 是一个值,可以带状态进去,比如一个要往里追加的 &mut Vec、一个已有的值、一份运行时才知道的 schema。文档第一句就是「DeserializeSeed is the stateful form of the Deserialize trait.」产出的类型是关联类型 Value,不一定是 Self。
无状态的 seed 就是 PhantomData<T>:
impl<'de, T> DeserializeSeed<'de> for PhantomData<T>
where
T: Deserialize<'de>,
{
type Value = T;
#[inline]
fn deserialize<D>(self, deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
T::deserialize(deserializer)
}
}
于是每个 Access 只要求格式实现带 seed 的版本,不带 seed 的便捷方法都是默认实现,传一个 PhantomData 进去,例如 next_element:
/// This method exists as a convenience for `Deserialize` implementations.
/// `SeqAccess` implementations should not override the default behavior.
#[inline]
fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
where
T: Deserialize<'de>,
{
self.next_element_seed(PhantomData)
}
next_key、next_value、next_entry、variant、newtype_variant 全是同样的一行。格式只写一份带 seed 的实现,有状态和无状态的调用方都能用。
这个 trait 定义在 de/mod.rs 里。serde_core 另有一个 private/seed.rs,里面只有一个 InPlaceSeed:包一个 &mut T,deserialize 里调 T::deserialize_in_place(上一页的原地反序列化就靠它)。这个文件 1.0.123 到 1.0.219 在 serde crate 里叫 de/seed.rs,1.0.220 拆出 serde_core 时挪进了 private/,derive 生成的代码经 _serde::__private229::de::InPlaceSeed 用它。
SeqAccess:一次取一个
SeqAccess 只有一个必需方法:
/// This returns `Ok(Some(value))` for the next value in the sequence, or
/// `Ok(None)` if there are no more remaining items.
///
/// `Deserialize` implementations should typically use
/// `SeqAccess::next_element` instead.
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'de>;
Ok(None) 表示「没有了」。序列有多长由格式说了算:JSON 读到 ] 返回 None,bincode 数到开头写的长度返回 None。Visitor 想要固定长度(元组、数组、按序列读的结构体)就只取那么多个,取不到就报 invalid_length。
T 是每次调用时才定的类型参数,同一个序列里每个元素可以按不同的类型读:(u8, String) 的 Visitor 第一次 next_element::<u8>()、第二次 next_element::<String>()。
size_hint
size_hint 默认返回 None:
/// Returns the number of elements remaining in the sequence, if known.
#[inline]
fn size_hint(&self) -> Option<usize> {
None
}
serde_json 的 SeqAccess 和 MapAccess 都不覆盖它:JSON 数组不读到 ] 不知道有几个元素。bincode 的序列先写长度,它的 SeqAccess 返回 Some(self.len)。Vec<T> 用它预分配,但不全信:先经过 size_hint::cautious,最多预分配 1 MiB(MAX_PREALLOC_BYTES),输入里声明的长度再大,预分配也不会超过这个数(见标准库类型的实现)。
1.0.0 之前,SeqVisitor::size_hint 的签名照抄 Iterator:fn size_hint(&self) -> (usize, Option<usize>),默认 (0, None)。issue #875 的理由是:「The size_hint for iterators is (lower, Option<upper>) because if you filter, take, chain, etc then you can get these elaborate constraints. That never happens when deserializing. The deserializer either knows ahead of time how many elements there are or it doesn't know.」PR #879 把它改成 Option<usize>,文档也从「Return the lower and upper bound of items remaining in the sequence.」改成现在这句「Returns the number of elements remaining in the sequence, if known.」,随 1.0.0 发布。
从 Iterator 转过来的地方用 size_hint::from_bounds:上下界相等才算知道长度,否则就是 None。
MapAccess:键和值分两步
MapAccess 有两个必需方法,next_key_seed 和 next_value_seed。两者必须交替调用,文档写明了顺序错了的后果:
/// # Panics
///
/// Calling `next_value_seed` before `next_key_seed` is incorrect and is
/// allowed to panic or return bogus results.
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: DeserializeSeed<'de>;
一次取一对的 next_entry_seed 有默认实现,就是两步连着调:
#[inline]
fn next_entry_seed<K, V>(
&mut self,
kseed: K,
vseed: V,
) -> Result<Option<(K::Value, V::Value)>, Self::Error>
where
K: DeserializeSeed<'de>,
V: DeserializeSeed<'de>,
{
match tri!(self.next_key_seed(kseed)) {
Some(key) => {
let value = tri!(self.next_value_seed(vseed));
Ok(Some((key, value)))
}
None => Ok(None),
}
}
它的文档说「MapAccess implementations should override the default behavior if a more efficient implementation is possible.」serde_json 没有覆盖;覆盖它的是 serde_core 的 de::value::MapDeserializer 和 serde 私有模块里给 Content 用的 MapDeserializer,它们手里已经有成对的键值。next_entry 再套一层 PhantomData。BTreeMap 和 HashMap 的 Visitor 用的就是 while let Some((key, value)) = tri!($access.next_entry())。
问: 既然 next_entry 一步就能取一对,为什么必需方法是两步的 next_key_seed / next_value_seed?
答: 因为有的 Visitor 要先看到键,才知道值该按什么类型读。derive 出来的结构体就是这样:next_key::<__Field>() 先把 "title" 认成 __Field::__field0,再在 match 的分支里调 next_value::<String>();认成 __Field::__ignore 的未知键,值按 IgnoredAny 跳过。每个字段类型不同,next_entry::<K, V>() 要求调用前就定好 V,做不到。HashMap<K, V> 的值类型是固定的,才能用 next_entry。
实验:同一个结构体,JSON 与 bincode
还是上一页的 LogDe 包装:每个 deserialize_*、visit_* 打印一行,visit_map / visit_seq 收到的 Access 也被包一层,next_*_seed 打印一行、返回时打印 -> Some 或 -> None,size_hint 打印返回值。src/bin/access.rs 的 struct 一段(M2 Max、macOS、rustc 1.99.0):
#[derive(Serialize, Deserialize, Debug)]
struct Post {
title: String,
tags: Vec<u8>,
}
// json::<T>(input):serde_json::Deserializer::from_str + LogDe
// bin(&value):bincode::serialize,再用 with_fixint_encoding 的 bincode::Deserializer + LogDe 读回来
json::<Post>(r#"{"title":"hi","tags":[1,2]}"#);
bin(&Post { title: "hi".into(), tags: vec![1, 2] });
== json {"title":"hi","tags":[1,2]}
deserialize_struct("Post", ["title", "tags"])
visit_map
next_key_seed
deserialize_identifier
visit_borrowed_str("title")
-> Some
next_value_seed
deserialize_string
visit_borrowed_str("hi")
next_key_seed
deserialize_identifier
visit_borrowed_str("tags")
-> Some
next_value_seed
deserialize_seq
visit_seq
size_hint() = None
next_element_seed
deserialize_u8
visit_u64(1)
-> Some
next_element_seed
deserialize_u8
visit_u64(2)
-> Some
next_element_seed
-> None
next_key_seed
-> None
=> Ok(Post { title: "hi", tags: [1, 2] })
== bincode [2, 0, 0, 0, 0, 0, 0, 0, 104, 105, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2]
deserialize_struct("Post", ["title", "tags"])
visit_seq
next_element_seed
deserialize_string
visit_string("hi")
-> Some
next_element_seed
deserialize_seq
visit_seq
size_hint() = Some(2)
next_element_seed
deserialize_u8
visit_u8(1)
-> Some
next_element_seed
deserialize_u8
visit_u8(2)
-> Some
next_element_seed
-> None
-> Some
=> Ok(Post { title: "hi", tags: [1, 2] })
几处对照:
- 同一个
deserialize_struct提示,JSON 回调visit_map,bincode 回调visit_seq。bincode 的deserialize_struct就是self.deserialize_tuple(fields.len(), visitor):字节流里没有字段名,只能按声明顺序读。derive 生成的__Visitor两个方法都实现了(见生成的 Deserialize)。 - JSON 的键经
next_key_seed→deserialize_identifier→visit_borrowed_str("title")变成__Field。serde_json 的deserialize_identifier就是deserialize_str。 - JSON 的 map 最后多一次
next_key_seed -> None,读到}才知道结束;bincode 按序列读结构体时只调两次next_element_seed,不会再问第三次。 Vec<u8>的Visitor先问size_hint:JSON 答None,bincode 答Some(2)。即使知道长度,Vec的Visitor仍会一直问到None,bincode 的SeqAccess数满 2 个后返回None。
map 一段读 BTreeMap<String, u8>,能看到 next_entry_seed:
== json {"a":1,"b":2}
deserialize_map
visit_map
next_entry_seed
deserialize_string
visit_borrowed_str("a")
deserialize_u8
visit_u64(1)
-> Some
next_entry_seed
deserialize_string
visit_borrowed_str("b")
deserialize_u8
visit_u64(2)
-> Some
next_entry_seed
-> None
=> Ok({"a": 1, "b": 2})
BTreeMap 的 Visitor 调 next_entry,默认实现转到包装层的 next_entry_seed(打印一行),包装层再调 serde_json 的 next_entry_seed,那是默认实现,内部依次调 serde_json 自己的 next_key_seed 和 next_value_seed,所以这两步没有被单独打印出来。
&mut A 也是 Access
SeqAccess 和 MapAccess 各有一个给可变引用的 blanket impl,impl SeqAccess for &mut A:
impl<'de, A> SeqAccess<'de> for &mut A
where
A: ?Sized + SeqAccess<'de>,
{
type Error = A::Error;
#[inline]
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'de>,
{
(**self).next_element_seed(seed)
}
// ...
}
MapAccess 的那个把七个方法全转一遍,连有默认实现的 next_entry、next_key 也转,这样内层覆盖过的实现不会被 &mut 这一层的默认实现绕过去。
为什么需要它:visit_seq<A: SeqAccess<'de>>(self, seq: A) 按值拿走 Access。格式如果想在 Visitor 用完之后再做点什么,就不能把自己交出去,只能交一个 &mut self。de::value::SeqDeserializer 就是这样,deserialize_any 在 Visitor 返回后调 end(),检查还有没有剩下的元素:
fn deserialize_any<V>(mut self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
let v = tri!(visitor.visit_seq(&mut self));
tri!(self.end());
Ok(v)
}
src/bin/seqend.rs:
use serde::Deserialize;
use serde::de::value::{Error, SeqDeserializer};
fn main() {
let de = SeqDeserializer::<_, Error>::new(vec![1u8, 2, 3].into_iter());
println!("{:?}", <(u8, u8)>::deserialize(de));
let de = SeqDeserializer::<_, Error>::new(vec![1u8, 2].into_iter());
println!("{:?}", <(u8, u8)>::deserialize(de));
}
Err(Error("invalid length 3, expected 2 elements in sequence"))
Ok((1, 2))
(u8, u8) 的 Visitor 取完两个就返回了,第三个元素是 end() 发现的。这些小 Deserializer 的用途在 value 模块与 IntoDeserializer。serde/src/private/de.rs 里重放 Content 的代码也大量用 visitor.visit_seq(&mut seq_visitor) 这个写法。
?Sized 是 1.0.130 加的(PR #2081,发布说明:「Provide MapAccess and SeqAccess impl for reference to a dynamically sized existing impl」)。EnumAccess 和 VariantAccess 没有这样的 impl:它们的方法都按值拿走 self,variant_seed 把自己换成 Variant,Variant 的四个方法各自把自己用掉,一个 enum 值只读一次。
EnumAccess 与 VariantAccess:先认变体,再取内容
EnumAccess 分两步。第一步 variant_seed 读出「是哪个变体」,同时交出一个 Variant:
pub trait EnumAccess<'de>: Sized {
type Error: Error;
type Variant: VariantAccess<'de, Error = Self::Error>;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'de>;
// ...
}
seed 产出的通常是 derive 生成的变体标识枚举 __Field,经 deserialize_identifier 读出来,和结构体字段名一样。第二步由 Visitor 按自己知道的变体形状,调 VariantAccess 四个方法中的一个:
| 方法 | 对应的变体 | 返回 |
|---|---|---|
unit_variant | Dot | Result<(), _> |
newtype_variant_seed | Circle(f64) | seed 的 Value |
tuple_variant | Pair(u8, u8) | 交给一个 Visitor,再走 visit_seq |
struct_variant | Rect { w, h } | 交给一个 Visitor,带字段表 |
两步分开的原因和 map 一样:变体的内容按什么形状读,要先知道是哪个变体才能定。形状由 Visitor 决定,格式只负责照办,输入对不上时报错。
serde_json 的 deserialize_enum 看第一个字节:" 说明是 "Dot" 这种只有名字的写法,交出 UnitVariantAccess;{ 说明是 {"Circle":1.5},交出 VariantAccess,它的 variant_seed 用 MapKey 读键、吃掉冒号,四个方法再读冒号后面的值。UnitVariantAccess 除了 unit_variant 返回 Ok(()),其余三个都直接报错:
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value>
where
T: de::DeserializeSeed<'de>,
{
Err(de::Error::invalid_type(
Unexpected::UnitVariant,
&"newtype variant",
))
}
bincode 的 variant_seed 读一个 u32 下标,用 idx.into_deserializer()(de::value::U32Deserializer)交给 seed,变体标识的 Visitor 收到的是 visit_u32。
实验:四种变体
src/bin/access.rs 的 enum 和 enum-err 两段:
#[derive(Serialize, Deserialize, Debug)]
enum Shape {
Dot,
Circle(f64),
Pair(u8, u8),
Rect { w: u8, h: u8 },
}
// "enum"
json::<Vec<Shape>>(r#"["Dot",{"Circle":1.5},{"Pair":[1,2]},{"Rect":{"w":3,"h":4}}]"#);
bin(&Shape::Rect { w: 3, h: 4 });
// "enum-err"
json::<Shape>(r#""Circle""#);
json::<Shape>(r#"{"Dot":null}"#);
json::<Shape>(r#"{"Dot":1}"#);
== json ["Dot",{"Circle":1.5},{"Pair":[1,2]},{"Rect":{"w":3,"h":4}}]
deserialize_seq
visit_seq
size_hint() = None
next_element_seed
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Dot")
unit_variant
-> Some
next_element_seed
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Circle")
newtype_variant_seed
deserialize_f64
visit_f64(1.5)
-> Some
next_element_seed
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Pair")
tuple_variant(2)
visit_seq
next_element_seed
deserialize_u8
visit_u64(1)
-> Some
next_element_seed
deserialize_u8
visit_u64(2)
-> Some
-> Some
next_element_seed
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Rect")
struct_variant(["w", "h"])
visit_map
next_key_seed
deserialize_identifier
visit_borrowed_str("w")
-> Some
next_value_seed
deserialize_u8
visit_u64(3)
next_key_seed
deserialize_identifier
visit_borrowed_str("h")
-> Some
next_value_seed
deserialize_u8
visit_u64(4)
next_key_seed
-> None
-> Some
next_element_seed
-> None
=> Ok([Dot, Circle(1.5), Pair(1, 2), Rect { w: 3, h: 4 }])
== bincode [3, 0, 0, 0, 3, 4]
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_u32(3)
struct_variant(["w", "h"])
visit_seq
next_element_seed
deserialize_u8
visit_u8(3)
-> Some
next_element_seed
deserialize_u8
visit_u8(4)
-> Some
=> Ok(Rect { w: 3, h: 4 })
== json "Circle"
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Circle")
newtype_variant_seed
=> Err(Error("invalid type: unit variant, expected newtype variant", line: 0, column: 0))
== json {"Dot":null}
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Dot")
unit_variant
=> Ok(Dot)
== json {"Dot":1}
deserialize_enum("Shape", ["Dot", "Circle", "Pair", "Rect"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Dot")
unit_variant
=> Err(Error("invalid type: integer `1`, expected unit", line: 1, column: 8))
- 每个变体都是
variant_seed在前、VariantAccess的一个方法在后,调哪个方法由 derive 生成的match决定,和输入长什么样无关。 "Circle"只有名字,Visitor却按Circle(f64)去调newtype_variant_seed,撞上UnitVariantAccess的报错。这个错误由de::Error::invalid_type构造,serde_json 的deserialize_enum没有像deserialize_any那样给它补位置,所以是line: 0, column: 0。{"Dot":null}能读成Dot:VariantAccess::unit_variant是de::Deserialize::deserialize(self.de),读一个(),JSON 的null正好是 unit。值换成1就报expected unit。- bincode 的
Rect是 4 字节下标3加两个u8。deserialize_identifier这一行来自U32Deserializer,不是 bincode:bincode 自己的deserialize_identifier直接报错「Bincode does not support Deserializer::deserialize_identifier」。struct_variant在 bincode 里同样按visit_seq读。
这是 enum 默认的外部标签表示走的路,derive 生成的完整代码见外部标签。内部标签和 untagged 不经过 EnumAccess,而是先把内容读进 Content 再重放,见内部标签与相邻标签和 untagged。
带状态的 seed:把二维数组读成一维
DeserializeSeed 文档里的例子是把 [[1, 2], [3, 4, 5], [6]] 读成一个 Vec<u64>。下面换一种写法:seed 自己同时也是内层数组的 Visitor,只持有一个 &mut Vec<u32>;外层 Visitor 对每个元素都新建一个 seed,传的是同一个 Vec。src/bin/seed.rs,计数分配器每次 alloc / realloc 加一:
// The seed carries state: the Vec every inner array is appended to.
struct AppendTo<'a>(&'a mut Vec<u32>);
impl<'de> DeserializeSeed<'de> for AppendTo<'_> {
type Value = ();
fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<(), D::Error> {
d.deserialize_seq(self)
}
}
// The same struct is also the Visitor for one inner array.
impl<'de> Visitor<'de> for AppendTo<'_> {
type Value = ();
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an array of integers")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<(), A::Error> {
while let Some(x) = seq.next_element::<u32>()? {
self.0.push(x);
}
Ok(())
}
}
// Outer array: hand every element the same Vec through a fresh seed.
struct Flatten;
impl<'de> Visitor<'de> for Flatten {
type Value = Vec<u32>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an array of arrays")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Vec<u32>, A::Error> {
let mut out = Vec::with_capacity(64);
while seq.next_element_seed(AppendTo(&mut out))?.is_some() {}
Ok(out)
}
}
fn main() {
let input = "[[1,2,3],[4],[],[5,6,7,8]]";
let before = ALLOCS.load(Relaxed);
let mut de = serde_json::Deserializer::from_str(input);
let flat = de.deserialize_seq(Flatten).unwrap();
let seed_allocs = ALLOCS.load(Relaxed) - before;
println!("seed: {flat:?} allocs={seed_allocs}");
let before = ALLOCS.load(Relaxed);
let nested: Vec<Vec<u32>> = serde_json::from_str(input).unwrap();
let parse_allocs = ALLOCS.load(Relaxed) - before;
let flat2: Vec<u32> = nested.into_iter().flatten().collect();
let nested_allocs = ALLOCS.load(Relaxed) - before;
println!("Vec<Vec<u32>>: {flat2:?} allocs={nested_allocs} (from_str {parse_allocs})");
let mut de = serde_json::Deserializer::from_str("[[1,2],[\"x\"]]");
println!("bad input: {}", de.deserialize_seq(Flatten).unwrap_err());
}
几个要点:
AppendTo的Value是():它不产出值,只往self.0里写。next_element_seed返回Some(())表示「读了一个内层数组」,None表示外层数组结束。- 每次循环都
AppendTo(&mut out)新建一个 seed,因为DeserializeSeed::deserialize按值拿走self。借用在这一次调用结束时就还回来了,下一轮可以再借。 - 内层元素用的是
next_element::<u32>(),也就是PhantomData<u32>这个 seed。有状态和无状态的 seed 可以在同一棵调用树里混用。 - 错误照常带上
expecting和位置:内层的u32遇到"x",报的是u32的expected u32,不是AppendTo的「an array of integers」。
平台 M2 Max、macOS、rustc 1.99.0,debug 构建。输入是 [[1,2,3],[4],[],[5,6,7,8]]。seed 一行只算 deserialize_seq(Flatten);Vec<Vec<u32>> 一行算 from_str 加上 flatten().collect(),括号里单独给出 from_str 的部分。allocs 各是多少?
serde 自己用 seed 的地方,最常见的就是上一页的 InPlaceSeed 和 derive 生成的 __Field 读法;缓冲主线里,Content 的读取和重放也用 seed 把「读成什么」带进 next_value_seed(见 Content:缓冲的代价)。
动脑筋答案
seed: [1, 2, 3, 4, 5, 6, 7, 8] allocs=1
Vec<Vec<u32>>: [1, 2, 3, 4, 5, 6, 7, 8] allocs=6 (from_str 4)
bad input: invalid type: string "x", expected u32 at line 1 column 11
- seed 版只有 1 次:
Vec::with_capacity(64)那一次。8 个数都追加进同一个缓冲,内层数组从来没有变成自己的Vec。 from_str的 4 次:外层Vec<Vec<u32>>1 次,三个非空的内层Vec各 1 次;空数组[]读成的Vec不分配。serde_json 的SeqAccess没有size_hint,Vec的Visitor从容量 0 开始push,这几个Vec的元素都不超过 4 个,第一次push分到的容量就够用,没有再realloc。- 剩下 2 次来自
flatten().collect()建新Vec和扩容。 - 第三行是错误路径,见上面最后一条要点。
小结
- 复合类型交给
Visitor的是一个 Access:SeqAccess逐个取元素,MapAccess先键后值,EnumAccess先认变体、再由VariantAccess的四个方法之一按形状取内容。长度和结束由格式决定,形状和类型由Visitor决定。 - 必需方法都带 seed。
DeserializeSeed是带self的Deserialize,PhantomData<T>是无状态的 seed,next_element、next_key、next_value、next_entry、variant、newtype_variant都是传PhantomData的默认实现。 - 这个 trait 在
de/mod.rs;private/seed.rs里只有InPlaceSeed。 size_hint默认None,JSON 不知道长度,bincode 知道;Vec经size_hint::cautious最多预分配 1 MiB。next_entry_seed默认就是next_key_seed加next_value_seed;结构体的Visitor必须两步走,因为值的类型取决于键。&mut A的 blanket impl 让格式交出&mut self之后还能收尾,SeqDeserializer靠它在visit_seq之后用end()检查多余元素;EnumAccess/VariantAccess按值消耗,没有这种 impl。