跳转到内容

Serde 整体架构

Serde 的关键架构是把 N 种 Rust 类型与 M 种数据格式从 N×M 个直接适配,拆成两组 trait 实现。类型只认识数据模型,格式也只认识数据模型。

先给答案:Serde 的关键架构是把 N 种 Rust 类型与 M 种数据格式从 N×M 个直接适配,拆成两组 tr…

Section titled “先给答案:Serde 的关键架构是把 N 种 Rust 类型与 M 种数据格式从 N×M 个直接适配,拆成两组 tr…”

Serde 的关键架构是把 N 种 Rust 类型与 M 种数据格式从 N×M 个直接适配,拆成两组 trait 实现。类型只认识数据模型,格式也只认识数据模型。 正文沿“组件地图 -> 双分派 -> N + M 而不是 N × M”展开:先确认入口和状态归属,再跟踪控制流或数据流的推进,最后落到对外可观察的结果。

主要失效边界集中在“把 Serde 当 JSON 库、依赖 serde::private、自定义格式只实现 deserializeany”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。

serde_core
Serialize / Serializer
Deserialize / Deserializer / Visitor / Access
^
|
serde ----+---- private compatibility helpers
optional re-export of derive macros
^
|
serde_derive
syn AST -> validated container model -> generated impl

Serialize 定义在 serde_core/src/ser/mod.rs:234-252,Serializer 在 :355-406;Deserialize<'de> 位于 serde_core/src/de/mod.rs:554-600,Deserializer<'de> 在 :945-1256,Visitor<'de> 在 :1317。derive 入口分别是 serde_derive/src/ser.rs:13 与 serde_derive/src/de.rs:25。

value.serialize(serializer)
-> value chooses semantic shape
-> serializer chooses wire representation
deserializer.deserialize_struct(..., visitor)
-> type chooses expected semantic shape
-> format drives visitor with parsed values

序列化由数据结构主动遍历自身;反序列化由格式解析输入,再回调目标类型提供的 Visitor。两边方向不同,是因为序列化已有完整值,而反序列化必须边读边构造。

扩展 需要实现
新数据结构 Serialize / Deserialize,通常 derive
新格式 Serializer / Deserializer + Error
新容器适配 实现标准 trait 后自动支持所有格式

复杂 enum tagging、flatten、untagged 尝试等无法总是单遍直达目标。Serde 在 serde/src/private/de.rs 提供 ContentVisitor(:290)、ContentDeserializer(:1041)和 FlatMapDeserializer(:3184)供 derive 代码使用。这些是实现细节,不是稳定公共 API。

替代方案:先把所有值转换成统一动态树(类似 JSON Value),再由每种格式读取或写出。

为什么不选:需要分配中间对象、丢失借用机会、二进制格式类型表达受限,并把错误推迟到运行时。

证据:Serialize 直接调用格式 Serializer,Deserialize 直接由 Deserializer 驱动 Visitor;只有 untagged/flatten 等复杂路径才局部使用 Content。

场景 现象 原因 规避
把 Serde 当 JSON 库 找不到 parser/writer 格式实现属于 serde_json 等 crate 分清协议层与格式层
依赖 serde::__private 升级破坏 私有 ABI 只服务 derive 同版本 只使用公开 trait/API
自定义格式只实现 deserialize_any 非自描述格式失败 输入不携带类型标签 实现 typed deserialize methods
复杂属性组合 额外分配/回溯 进入 Content 缓冲路径 在热路径选择简单表示

当两个开放集合需要互操作时,可定义稳定中间协议,而不是统一中间对象。协议允许双方直接对话,编译器还能内联掉大量适配层。

面试锚点

  • Serde 为什么是 N+M 适配复杂度?
  • 序列化与反序列化的控制方向为何不同?
  • Content 为什么只应是局部逃生舱?