Rust in Everything
基于 serde 1.0.229

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_seqnext_element_seed
MapAccess<'de>格式 → visit_mapnext_key_seed、next_value_seed
EnumAccess<'de>格式 → visit_enumvariant_seed,外加关联类型 Variant
VariantAccess<'de>variant_seed 的返回值之一unit_variant、newtype_variant_seed、tuple_variant、struct_variant
📝NOTE
冷知识:它们以前都叫 Visitor

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),输入里声明的长度再大,预分配也不会超过这个数(见标准库类型的实现)。

📝NOTE
冷知识:size_hint 原来是一对上下界

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())。

📝NOTE
没有蠢问题

问: 既然 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_variantDotResult<(), _>
newtype_variant_seedCircle(f64)seed 的 Value
tuple_variantPair(u8, u8)交给一个 Visitor,再走 visit_seq
struct_variantRect { 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」。
❗IMPORTANT
动脑筋:两种写法各分配几次?

平台 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。

💬 关联讨论

发起讨论
加载中...