Rust in Everything
基于 serde 1.0.229

一次往返

主线位置:序列化主线和反序列化主线各从头走到尾一遍。前半页是一次 serde_json::to_string:derive 生成的代码把数据推给 Serializer;后半页是一次 serde_json::from_str:serde_json 读字节、回调 derive 生成的 Visitor。每一步只讲到「谁调了谁」,trait 本身在 02 核心 trait,生成代码的细节在 生成的 Serialize 和 生成的 Deserialize。

sequenceDiagram
  participant U as 调用方
  participant P as Post 的 impl
  participant J as serde_json
  U->>J: to_string(post)
  J->>P: serialize(ser)
  P->>J: serialize_struct
  P->>J: serialize_field 三次
  P->>J: end
  J-->>U: 字符串
  U->>J: from_str(s)
  J->>P: deserialize(de)
  P->>J: deserialize_struct(visitor)
  J->>P: visit_map(MapAccess)
  P->>J: next_key 与 next_value
  J-->>U: Post

例子

类型和上一页serde 解决什么问题里的一样:一个 struct,字段里有一个 String、一个 Vec<String> 和一个 enum。enum 一个变体不带数据,一个带一个 u32,覆盖了数据模型里的 struct、string、seq、unit_variant、newtype_variant、u32。

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Post {
  pub title: String,
  pub tags: Vec<String>,
  pub status: Status,
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum Status {
  Draft,
  Published(u32),
}

src/bin/round_trip.rs:

use exp_serde_overview::model::{Post, Status};

fn main() {
  let post = Post {
    title: "serde".to_string(),
    tags: vec!["rust".to_string(), "json".to_string()],
    status: Status::Published(2026),
  };
  let json = serde_json::to_string(&post).unwrap();
  println!("{json}");
  let back: Post = serde_json::from_str(&json).unwrap();
  println!("{back:?}");
  println!("equal: {}", back == post);
  let draft: Status = serde_json::from_str(r#""Draft""#).unwrap();
  println!("{draft:?} -> {}", serde_json::to_string(&draft).unwrap());
}

M2 Max、macOS、rustc 1.99.0,serde =1.0.229、serde_json =1.0.154:

{"title":"serde","tags":["rust","json"],"status":{"Published":2026}}
Post { title: "serde", tags: ["rust", "json"], status: Published(2026) }
equal: true
Draft -> "Draft"

带数据的变体写成 {"Published":2026},不带数据的写成字符串 "Draft"。这是 serde 默认的外部标签表示,其他表示在 04 enum 的表示与缓冲。

生成的 Serialize

cargo expand --lib model 展开 Post 的 Serialize(只摘函数体,去掉了外层的 const _: () = { extern crate serde as _serde; ... } 包装和属性):

        impl _serde::Serialize for Post {
            fn serialize<__S>(
                &self,
                __serializer: __S,
            ) -> _serde::__private229::Result<__S::Ok, __S::Error>
            where
                __S: _serde::Serializer,
            {
                let mut __serde_state = _serde::Serializer::serialize_struct(
                    __serializer,
                    "Post",
                    false as usize + 1 + 1 + 1,
                )?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __serde_state,
                    "title",
                    &self.title,
                )?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __serde_state,
                    "tags",
                    &self.tags,
                )?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __serde_state,
                    "status",
                    &self.status,
                )?;
                _serde::ser::SerializeStruct::end(__serde_state)
            }
        }

三步:serialize_struct 拿到一个状态对象 __serde_state,对每个字段调一次 serialize_field(键, &值),最后 end。生成这段代码的模板是 serde_derive 的 serialize_struct_as_struct,核心就是一个 quote_block!:

    quote_block! {
        let #let_mut __serde_state = _serde::Serializer::serialize_struct(__serializer, #type_name, #len)?;
        #tag_field
        #(#serialize_fields)*
        _serde::ser::SerializeStruct::end(__serde_state)
    }

#serialize_fields 由 serialize_struct_visitor 逐字段生成;#len 是一个 fold 出来的表达式,起点是 #tag_field_exists as usize,每个字段加 1(带 skip_serializing_if 的字段加 if 条件 { 0 } else { 1 })。所以展开结果里是 false as usize + 1 + 1 + 1 而不是 3,编译器会把它折成常量。

📝NOTE
冷知识:false as usize 是修一个 flatten 丢标签的 bug 留下的

#1474(「Fixed #1468, flattened struct fields made structs ignore their tag」,首次发布在 1.0.87)之前,字段数的起点是一个整数字面量 additional_field_count:#[serde(tag = "...")] 的 struct 是 1,否则是 0,标签字段也只在 serialize_struct_as_struct 里插进去。#1468 报告的是:struct 带了 tag,又有一个 #[serde(flatten)] 字段,输出里就没有标签了,因为带 flatten 的 struct 走的是另一个函数 serialize_struct_as_map,那里没有插标签的代码。修复把插标签抽成 serialize_struct_tag_field,两条路径都调它,字段数的起点也统一改成 quote!(#tag_field_exists as usize)。PR 描述说这次也顺手「overhauls my initial implementation a bit to make it a bit cleaner an easier to read」;1.0.87 的 release notes 写的是「Fix inclusion of tag in struct containing flattened fields (#1468)」。1.0.229 里没有标签的 struct 展开出来就是 false as usize + ...。

enum 的展开是一个 match,每个变体一个分支,模板是 serialize_externally_tagged_variant:

                match *self {
                    Status::Draft => {
                        _serde::Serializer::serialize_unit_variant(
                            __serializer,
                            "Status",
                            0u32,
                            "Draft",
                        )
                    }
                    Status::Published(ref __field0) => {
                        _serde::Serializer::serialize_newtype_variant(
                            __serializer,
                            "Status",
                            1u32,
                            "Published",
                            __field0,
                        )
                    }
                }

类型名、变体序号、变体名一起交出去,格式自己挑(上一页的 bincode 只用了序号)。

序列化主线:一次 to_string

to_string 调 to_vec,to_vec 先 Vec::with_capacity(128),再调 to_writer:

pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
where
    W: io::Write,
    T: ?Sized + Serialize,
{
    let mut ser = Serializer::new(writer);
    value.serialize(&mut ser)
}

Serializer::new 用的是 CompactFormatter。实现 serde::Serializer 的是 &mut Serializer<W, F>,不是 Serializer<W, F> 本身:trait 的方法都按值拿 self,传一个可变引用进去,嵌套的值递归调用时可以反复 &mut *self 借出来。to_string 最后用 String::from_utf8_unchecked 把字节转成字符串,注释写着「We do not emit invalid UTF-8.」。

打印每一次调用

要看清 Post::serialize 和 serde_json 之间到底发生了什么,写一个包装器:LogSer<S> 自己实现 Serializer,每个方法先打印,再转给里面的 S。难点是递归:serialize_field("tags", &self.tags) 里的 Vec 会对 serde_json 的 Serializer 调 serialize_seq,绕过包装器。解决办法是把每个往下传的值包一层 Val,它的 Serialize 实现把内层的 serializer 再包成 LogSer:

/// `Val(&x).serialize(s)` serializes `x` into `LogSer(s)`: this is how the
/// wrapper follows the recursion into nested values.
pub struct Val<'a, T: ?Sized>(pub &'a T);

impl<T: ?Sized + Serialize> Serialize for Val<'_, T> {
  fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
    self.0.serialize(LogSer(s))
  }
}

pub struct LogSer<S>(pub S);
pub struct LogCompound<C>(C);

impl<S: Serializer> Serializer for LogSer<S> {
  type Ok = S::Ok;
  type Error = S::Error;
  type SerializeSeq = LogCompound<S::SerializeSeq>;
  // ... 其余六个复合类型同样包一层 LogCompound
  type SerializeStruct = LogCompound<S::SerializeStruct>;

  // serialize_bool ... serialize_bytes 由一个宏生成:打印方法名和参数,再转发
  fn serialize_newtype_variant<T: ?Sized + Serialize>(
    self,
    name: &'static str,
    index: u32,
    variant: &'static str,
    value: &T,
  ) -> Result<S::Ok, S::Error> {
    log(format!("serialize_newtype_variant({name:?}, {index}, {variant:?})"));
    with_depth(|| self.0.serialize_newtype_variant(name, index, variant, &Val(value)))
  }
  fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct, S::Error> {
    log(format!("serialize_struct({name:?}, {len})"));
    let inner = with_depth(|| self.0.serialize_struct(name, len))?;
    DEPTH.with(|d| d.set(d.get() + 1));
    Ok(LogCompound(inner))
  }
  // ... 其余方法同一模式
}

impl<C: ser::SerializeStruct> ser::SerializeStruct for LogCompound<C> {
  type Ok = C::Ok;
  type Error = C::Error;
  fn serialize_field<T: ?Sized + Serialize>(&mut self, key: &'static str, value: &T) -> Result<(), C::Error> {
    log(format!("serialize_field({key:?})"));
    with_depth(|| self.0.serialize_field(key, &Val(value)))
  }
  fn end(self) -> Result<C::Ok, C::Error> {
    end_compound(|| self.0.end())
  }
}

log 按一个线程局部的深度缩进打印,with_depth 在调用期间把深度加一,end_compound 先减一再打印 end。另外给 serde_json 一个 LogWriter 当输出,它的 io::Write::write 把每次写入的字节打印成 write "...",这样能看到每次调用写了什么。src/bin/log_ser.rs:

use exp_serde_overview::log_ser::{LogWriter, Val};
use exp_serde_overview::model::{Post, Status};
use serde::Serialize;

fn main() {
  let post = Post {
    title: "serde".to_string(),
    tags: vec!["rust".to_string(), "json".to_string()],
    status: Status::Published(2026),
  };
  let mut ser = serde_json::Serializer::new(LogWriter(Vec::new()));
  Val(&post).serialize(&mut ser).unwrap();
  let out = ser.into_inner().0;
  println!("output: {}", String::from_utf8(out).unwrap());
}

输出(M2 Max、macOS、rustc 1.99.0,跑了 3 次,完全相同):

serialize_struct("Post", 3)
  write "{"
  serialize_field("title")
    write "\""
    write "title"
    write "\""
    write ":"
    serialize_str("serde")
    write "\""
    write "serde"
    write "\""
  serialize_field("tags")
    write ","
    write "\""
    write "tags"
    write "\""
    write ":"
    serialize_seq(Some(2))
      write "["
      serialize_element
        serialize_str("rust")
        write "\""
        write "rust"
        write "\""
      serialize_element
        write ","
        serialize_str("json")
        write "\""
        write "json"
        write "\""
    end
      write "]"
  serialize_field("status")
    write ","
    write "\""
    write "status"
    write "\""
    write ":"
    serialize_newtype_variant("Status", 1, "Published")
      write "{"
      write "\""
      write "Published"
      write "\""
      write ":"
      serialize_u32(2026)
      write "2026"
      write "}"
end
  write "}"
output: {"title":"serde","tags":["rust","json"],"status":{"Published":2026}}

逐步对照

1. serialize_struct("Post", 3) 写 {。 serde_json 的 serialize_struct 不关心名字 "Post",只在开了 arbitrary_precision / raw_value 时认两个魔法名字,其余一律转给 serialize_map(Some(len))。serialize_map 调 formatter.begin_object 写 {,返回 Compound::Map { ser, state: State::First }。len 只用来判断是不是 0:是 0 就当场写 },状态记成 State::Empty。Compound 就是那个状态对象,七个复合类型的关联类型在 serde_json 里都是它。

2. serialize_field("title") 写 "title":"serde"。 SerializeStruct::serialize_field 对 Compound::Map 直接转给 SerializeMap::serialize_entry,而 serde 里 serialize_entry 的默认实现是先 serialize_key 再 serialize_value。serde_json 的 serialize_key 先调 begin_object_key(writer, first),CompactFormatter 用的是 Formatter 的默认实现:第一个键什么也不写,之后的键前面写 ,;然后把状态改成 State::Rest,用 MapKeySerializer 写键。键 "title" 是直接交给 serde_json 内部的,所以日志里没有对应的 serialize_str,只有三次 write:format_escaped_str 分别写开引号、内容、闭引号。serialize_value 写 :,再 value.serialize(&mut **ser):String 的 Serialize 就是 serializer.serialize_str(self),日志里的 serialize_str("serde") 是值这一侧经过包装器的那次调用。

3. serialize_field("tags") 写 ,"tags":[...]。 这次 first 是 false,先写 ,。Vec<T> 的 Serialize 由 seq_impl! 生成,函数体是 serializer.collect_seq(self);collect_seq 是 Serializer 的默认方法,用迭代器的长度提示调 serialize_seq(Some(2)),逐个 serialize_element,最后 end。serde_json 的 serialize_seq 写 [,serialize_element 在第二个元素前写 ,,end 写 ]。日志里第二个 serialize_element 下面先出现 write "," 再出现 serialize_str("json"),就是这个顺序。

4. serialize_field("status") 写 ,"status":{"Published":2026}。 Status 的 Serialize 调 serialize_newtype_variant,serde_json 把它写成只有一个键的对象:{、变体名、:、值、}。u32 写成 2026 只有一次 write,数字不需要转义。

5. end 写 }。 SerializeStruct::end 转给 SerializeMap::end,状态不是 Empty 就写 }。

整个过程里没有任何中间结构:derive 生成的代码每调一次方法,serde_json 当场就把对应的字节写进 Vec。逗号、冒号、缩进全由 Formatter 决定,换成 to_string_pretty 只是换成 PrettyFormatter,Post 的代码一行不变(serde_json 的 Serializer)。

生成的 Deserialize

反方向的展开长得多(Post 一个类型就有两百多行)。只摘骨架,细节(__Field 的三种 visit_*、visit_seq、missing_field 的行为)在 生成的 Deserialize:

        impl<'de> _serde::Deserialize<'de> for Post {
            fn deserialize<__D>(
                __deserializer: __D,
            ) -> _serde::__private229::Result<Self, __D::Error>
            where
                __D: _serde::Deserializer<'de>,
            {
                enum __Field {
                    __field0,
                    __field1,
                    __field2,
                    __ignore,
                }
                struct __FieldVisitor;
                impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
                    type Value = __Field;
                    // ... expecting、visit_u64
                    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),
                            "status" => _serde::__private229::Ok(__Field::__field2),
                            _ => _serde::__private229::Ok(__Field::__ignore),
                        }
                    }
                    // ... visit_bytes
                }
                impl<'de> _serde::Deserialize<'de> for __Field {
                    #[inline]
                    fn deserialize<__D>(
                        __deserializer: __D,
                    ) -> _serde::__private229::Result<Self, __D::Error>
                    where
                        __D: _serde::Deserializer<'de>,
                    {
                        _serde::Deserializer::deserialize_identifier(
                            __deserializer,
                            __FieldVisitor,
                        )
                    }
                }
                struct __Visitor<'de> {
                    marker: _serde::__private229::PhantomData<Post>,
                    lifetime: _serde::__private229::PhantomData<&'de ()>,
                }
                impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
                    type Value = Post;
                    // ... expecting("struct Post")、visit_seq
                    #[inline]
                    fn visit_map<__A>(
                        self,
                        mut __map: __A,
                    ) -> _serde::__private229::Result<Self::Value, __A::Error>
                    where
                        __A: _serde::de::MapAccess<'de>,
                    {
                        let mut __field0: _serde::__private229::Option<String> = _serde::__private229::None;
                        let mut __field1: _serde::__private229::Option<Vec<String>> = _serde::__private229::None;
                        let mut __field2: _serde::__private229::Option<Status> = _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、__field2 同样
                                _ => {
                                    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、__field2 同样
                        _serde::__private229::Ok(Post {
                            title: __field0,
                            tags: __field1,
                            status: __field2,
                        })
                    }
                }
                const FIELDS: &'static [&'static str] = &["title", "tags", "status"];
                _serde::Deserializer::deserialize_struct(
                    __deserializer,
                    "Post",
                    FIELDS,
                    __Visitor {
                        marker: _serde::__private229::PhantomData::<Post>,
                        lifetime: _serde::__private229::PhantomData,
                    },
                )
            }
        }

deserialize 函数体里定义了三个局部类型(__Field、__FieldVisitor、__Visitor),真正执行的只有最后一句:把 __Visitor 交给 deserialize_struct。之后读什么、什么时候回调,都由格式决定。模板在 serde_derive 的 de/struct_.rs:visit_map 的循环由 deserialize_map 生成(while let ... next_key::<__Field> 那一行),__Field 和 __FieldVisitor 由 de/identifier.rs 的 deserialize_generated 生成。缺字段时调的 missing_field 在 serde crate 的 private/de.rs 里,是 derive 的运行时,不在 serde_core。

Status 的 Deserialize 结构相同,最后一句是 deserialize_enum(__deserializer, "Status", VARIANTS, __Visitor),__Visitor 实现的是 visit_enum:

                        match _serde::de::EnumAccess::variant(__data) {
                            _serde::__private229::Ok((__Field::__field0, __variant)) => {
                                _serde::de::VariantAccess::unit_variant(__variant)?;
                                _serde::__private229::Ok(Status::Draft)
                            }
                            _serde::__private229::Ok((__Field::__field1, __variant)) => {
                                _serde::__private229::Result::map(
                                    _serde::de::VariantAccess::newtype_variant::<
                                        u32,
                                    >(__variant),
                                    Status::Published,
                                )
                            }
                            _serde::__private229::Err(__err) => {
                                _serde::__private229::Err(__err)
                            }
                        }

先读出是哪个变体,再按变体的形状要内容:EnumAccess 和 VariantAccess 的分工在 SeqAccess、MapAccess 与 EnumAccess。

反序列化主线:一次 from_str

from_str 把 &str 包成 StrRead,交给 from_trait:

fn from_trait<'de, R, T>(read: R) -> Result<T>
where
    R: Read<'de>,
    T: de::Deserialize<'de>,
{
    let mut de = Deserializer::new(read);
    let value = tri!(de::Deserialize::deserialize(&mut de));

    // Make sure the whole stream has been consumed.
    tri!(de.end());
    Ok(value)
}

和序列化一样,实现 serde::Deserializer 的是 &mut Deserializer<R>。Post::deserialize 返回之后还有一步 end:值后面除了空白还有别的字符,就报 trailing characters。

打印每一次调用

反方向的包装器要包四种东西:格式的 Deserializer(类型调它的 deserialize_*)、类型的 Visitor(格式调它的 visit_*)、格式交给 Visitor 的 MapAccess / SeqAccess / EnumAccess / VariantAccess,以及 DeserializeSeed。最后一种是递归的入口:next_key_seed(seed) 里 serde_json 会用一个内部的 Deserializer(键用 MapKey)调 seed.deserialize(..),包一层 LogSeed 才能把那个 Deserializer 也换成 LogDe:

/// Wraps the format's Deserializer: the type calls `deserialize_*` on it.
pub struct LogDe<D>(pub D);
/// Wraps the type's Visitor: the format calls `visit_*` on it.
pub struct LogVis<V>(V);
/// Wraps a DeserializeSeed so that the nested Deserializer is wrapped too.
pub struct LogSeed<T>(T);
pub struct LogAccess<A>(A);

impl<'de, D: Deserializer<'de>> Deserializer<'de> for LogDe<D> {
  type Error = D::Error;
  // deserialize_any ... deserialize_ignored_any 中只带 visitor 的那些由宏生成
  fn deserialize_struct<V: Visitor<'de>>(
    self,
    name: &'static str,
    fields: &'static [&'static str],
    visitor: V,
  ) -> Result<V::Value, D::Error> {
    log(format!("deserialize_struct({name:?}, {fields:?})"));
    indented(|| self.0.deserialize_struct(name, fields, LogVis(visitor)))
  }
  // ...
}

impl<'de, V: Visitor<'de>> Visitor<'de> for LogVis<V> {
  type Value = V::Value;
  // expecting 直接转发;visit_bool ... visit_byte_buf 由宏生成
  fn visit_map<A: de::MapAccess<'de>>(self, map: A) -> Result<V::Value, A::Error> {
    log("visit_map");
    indented(|| self.0.visit_map(LogAccess(map)))
  }
  // ...
}

impl<'de, T: DeserializeSeed<'de>> DeserializeSeed<'de> for LogSeed<T> {
  type Value = T::Value;
  fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<T::Value, D::Error> {
    self.0.deserialize(LogDe(d))
  }
}

impl<'de, A: de::MapAccess<'de>> de::MapAccess<'de> for LogAccess<A> {
  type Error = A::Error;
  fn next_key_seed<K: DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>, A::Error> {
    log("next_key_seed");
    let r = indented(|| self.0.next_key_seed(LogSeed(seed)));
    if let Ok(None) = r {
      log("  => None");
    }
    r
  }
  fn next_value_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<T::Value, A::Error> {
    log("next_value_seed");
    indented(|| self.0.next_value_seed(LogSeed(seed)))
  }
  // ... size_hint 直接转发
}
// SeqAccess、EnumAccess、VariantAccess 同一模式

包装器的每个 visit_* 都原样转给内层的同名方法,所以日志记录的是格式调了哪个方法;内层 Visitor 收到之后再转给哪个默认方法,不在日志里。src/bin/log_de.rs:

use exp_serde_overview::log_de::LogDe;
use exp_serde_overview::model::Post;
use serde::Deserialize;

fn main() {
  let json = r#"{"title":"serde","tags":["rust","json"],"status":{"Published":2026}}"#;
  let mut de = serde_json::Deserializer::from_str(json);
  let post = Post::deserialize(LogDe(&mut de)).unwrap();
  de.end().unwrap();
  println!("{post:?}");
}

输出(M2 Max、macOS、rustc 1.99.0,跑了 3 次,完全相同):

deserialize_struct("Post", ["title", "tags", "status"])
  visit_map
    next_key_seed
      deserialize_identifier
        visit_borrowed_str("title")
    next_value_seed
      deserialize_string
        visit_borrowed_str("serde")
    next_key_seed
      deserialize_identifier
        visit_borrowed_str("tags")
    next_value_seed
      deserialize_seq
        visit_seq
          next_element_seed
            deserialize_string
              visit_borrowed_str("rust")
          next_element_seed
            deserialize_string
              visit_borrowed_str("json")
          next_element_seed
            => None
    next_key_seed
      deserialize_identifier
        visit_borrowed_str("status")
    next_value_seed
      deserialize_enum("Status", ["Draft", "Published"])
        visit_enum
          variant_seed
            deserialize_identifier
              visit_borrowed_str("Published")
          newtype_variant_seed
            deserialize_u32
              visit_u64(2026)
    next_key_seed
      => None
Post { title: "serde", tags: ["rust", "json"], status: Published(2026) }

和序列化的日志对比,缩进的方向反过来了:序列化时最外层是类型调格式(serialize_struct),往里还是类型调格式;反序列化时类型只调了一次 deserialize_struct,下一层就是格式调类型(visit_map),再下一层类型又调格式(next_key_seed)。控制权在两边之间来回交了好几次。

逐步对照

1. deserialize_struct → visit_map。 serde_json 的 deserialize_struct 跳过空白看下一个字节:{ 就吃掉它,调 visitor.visit_map(MapAccess::new(self));[ 就调 visit_seq;别的字节报 invalid type。visit_map 返回后再调 end_map 吃掉 }。name 和 fields 两个参数 serde_json 都没用(参数名是 _name、_fields)。非自描述格式要用它们:bincode 1.3.3 的 deserialize_struct 就是 self.deserialize_tuple(fields.len(), visitor),按字段数当成元组读。

2. next_key_seed → deserialize_identifier → visit_borrowed_str("title")。 生成代码调的是 next_key::<__Field>(),它是 MapAccess 的默认方法,转给 next_key_seed(PhantomData);PhantomData<T> 作为 seed 时就是 T::deserialize(deserializer)。serde_json 的 next_key_seed 先判断还有没有键:看到 } 返回 None;第一个键必须以 " 开头,之后的键前面必须有 ,;各种不合法的情况分别报 KeyMustBeAString、TrailingComma、ExpectedObjectCommaOrEnd。有键就用 MapKey 这个专门读键的 Deserializer 调 seed。__Field::deserialize 调 deserialize_identifier,MapKey 把它连同 str、string 等一起用 forward_to_deserialize_any! 转给自己的 deserialize_any:读出字符串,没有转义时是 Reference::Borrowed,调 visit_borrowed_str。__FieldVisitor 没实现 visit_borrowed_str,默认实现转给 visit_str,match 出 __Field::__field0。

3. next_value_seed → deserialize_string → visit_borrowed_str("serde")。 next_value_seed 先 parse_object_colon 吃掉 :,再用主 Deserializer 调 seed。String 的 Deserialize 调 deserialize_string(StringVisitor);serde_json 的 deserialize_string 直接转给 deserialize_str,按能不能借用调 visit_borrowed_str 或 visit_str。StringVisitor 拿到借用的 &str 后 to_owned() 一次。

📝NOTE
没有蠢问题

问: title 是 String,日志里 serde_json 调的却是 visit_borrowed_str,难道 String 借用了输入?

答: 没有。调哪个 visit_* 是格式根据输入决定的:from_str 的输入是 &'de str,字符串里没有转义时,serde_json 可以直接切出一段 &'de str,于是它调 visit_borrowed_str,意思是「这段可以借,借不借由你」。String 的 visitor 不借,它只实现了 visit_str(和 visit_string),visit_borrowed_str 的默认实现转给 visit_str,在那里 to_owned()。把字段改成 &'de str 时,visitor 会实现 visit_borrowed_str 并直接收下这段借用;输入里有转义(serde_json 只能把解码后的内容放进内部的 scratch 缓冲,调 visit_str)时,同一个字段就会报错。这些在 生命周期 里讲。

4. tags:deserialize_seq → visit_seq → 三次 next_element_seed。 Vec<T> 的 Deserialize 调 deserialize_seq,它的 visitor 在 visit_seq 里 while let Some(value) = seq.next_element()? 一直取到 None。serde_json 的 deserialize_seq 看到 [ 才调 visit_seq。两个元素,三次 next_element_seed:最后一次读到 ],返回 None。

5. status:deserialize_enum → visit_enum → variant_seed → newtype_variant_seed。 serde_json 的 deserialize_enum 看到 { 时调 visit_enum(VariantAccess::new(self)),看到 " 时调 visit_enum(UnitVariantAccess::new(self))("Draft" 走这条)。variant_seed 也用 MapKey 读变体名,再吃掉 :;newtype_variant_seed 就是 seed.deserialize(self.de)。u32 调 deserialize_u32,serde_json 把 i8 到 u64 的整数提示都转给 deserialize_number,放得进 u64 的非负整数一律回调 visit_u64。u32 的 visitor 用 uint_to_self!(u64:visit_u64) 实现了 visit_u64:u32::try_from(v),放不下就报 invalid value。这就是上一页说的「提示和回调不必一一对应」。

6. 最后一次 next_key_seed 返回 None。 看到 },while let 结束。三个 Option 都是 Some,不走 missing_field,构造出 Post。回到 deserialize_struct,end_map 吃掉 };回到 from_trait,end 确认后面没有别的字符。

❗IMPORTANT
动脑筋:JSON 里多了一个未知字段,它的值会让 Visitor 收到几次 visit_* 调用?

平台 M2 Max、macOS、rustc 1.99.0。还是同一个 Post 和同一个 LogDe 包装器,换一段输入:字段顺序打乱,多一个 Post 没有的字段 extra,它的值是一个嵌套的对象;status 用不带数据的变体。

use exp_serde_overview::log_de::LogDe;
use exp_serde_overview::model::Post;
use serde::Deserialize;

fn main() {
  // Fields out of order, one unknown key, a unit variant.
  let json = r#"{"tags":[],"extra":{"a":[1,2]},"title":"x","status":"Draft"}"#;
  let mut de = serde_json::Deserializer::from_str(json);
  let post = Post::deserialize(LogDe(&mut de)).unwrap();
  de.end().unwrap();
  println!("{post:?}");
}

extra 的值 {"a":[1,2]} 里有一个对象、一个键、一个数组、两个数。next_value_seed 下面会出现几行 visit_*?

动脑筋答案

deserialize_struct("Post", ["title", "tags", "status"])
  visit_map
    next_key_seed
      deserialize_identifier
        visit_borrowed_str("tags")
    next_value_seed
      deserialize_seq
        visit_seq
          next_element_seed
            => None
    next_key_seed
      deserialize_identifier
        visit_borrowed_str("extra")
    next_value_seed
      deserialize_ignored_any
        visit_unit
    next_key_seed
      deserialize_identifier
        visit_borrowed_str("title")
    next_value_seed
      deserialize_string
        visit_borrowed_str("x")
    next_key_seed
      deserialize_identifier
        visit_borrowed_str("status")
    next_value_seed
      deserialize_enum("Status", ["Draft", "Published"])
        visit_enum
          variant_seed
            deserialize_identifier
              visit_borrowed_str("Draft")
          unit_variant
    next_key_seed
      => None
Post { title: "x", tags: [], status: Draft }

答案是 1 次:visit_unit。

  • "extra" 经 __FieldVisitor::visit_str 匹配到 _ => __Field::__ignore,生成代码对它调 next_value::<IgnoredAny>()。IgnoredAny 的 Deserialize 调 deserialize_ignored_any。
  • serde_json 的 deserialize_ignored_any 调 ignore_value 在字节层面跳过整个值(括号配对、字符串、数字都只扫过去,不构造任何东西),然后只调一次 visitor.visit_unit()。对象、键、数组、两个数,一个都没有交给 Visitor。换一个不会「跳过」的格式,IgnoredAny 的 visitor 也能逐个接收再丢掉,但 serde_json 选择了直接跳过。
  • 字段顺序打乱没有影响:visit_map 的 while let 按 JSON 里的顺序取键,match 到哪个就填哪个 Option。tags 是空数组,next_element_seed 只调一次就返回 None。
  • "Draft" 以 " 开头,deserialize_enum 走 UnitVariantAccess,variant_seed 读出变体名,unit_variant 什么也不用读。

小结

  • derive 生成的 Serialize 是三步:serialize_struct(name, len) 拿状态对象,每个字段 serialize_field(键, &值),最后 end;len 展开成 false as usize + 1 + 1 + 1,起点 tag_field_exists as usize 来自 1.0.87 的 #1474。enum 是一个 match,每个变体交出类型名、序号和变体名。
  • serde_json 的 serialize_struct 转给 serialize_map,写 {;serialize_field 经 serialize_entry 拆成 serialize_key(,、键)和 serialize_value(:、值);end 写 }。日志证明每次调用当场写出字节,没有中间结构。
  • derive 生成的 Deserialize 定义 __Field、__FieldVisitor、__Visitor 三个局部类型,只执行一句 deserialize_struct(.., __Visitor);之后由 serde_json 回调 visit_map,visit_map 再循环调 next_key / next_value,控制权在格式和类型之间来回交接。
  • 键经 deserialize_identifier 由 MapKey 转给 deserialize_any 读出,visit_borrowed_str 默认转 visit_str 后匹配成 __Field;u32 字段收到的是 visit_u64(2026),由 u32 的 visitor 检查范围。
  • 未知字段走 IgnoredAny,serde_json 在字节层面跳过整个值,只回调一次 visit_unit。

💬 关联讨论

发起讨论
加载中...