Serde 元编程:从过程宏到零成本抽象
从编译期元编程视角剖析 Serde 的工程实现。
几乎每一个 Rust 项目都依赖 Serde。它的使用极其朴素——写一行 #[derive(Serialize, Deserialize)],任何数据结构就能在 JSON、YAML、TOML、Bincode、MessagePack 之间自由流转。但在这行轻量语法背后,隐藏着 Rust 生态最精巧的工程设计之一:一套由过程宏、trait、泛型和单态化共同构建的零成本抽象。
本专栏不教读者"怎么用 Serde"。本专栏拆解 Serde 源码——从 syn 解析 AST、quote 生成 TokenStream,到 Data Model 如何把"M 种数据结构 × N 种格式"的组合爆炸降为 M+N 的实现复杂度;从 Serializer/Deserializer trait 的边界设计,到 Visitor 模式为什么是反序列化的唯一正解;从属性宏 #[serde(rename)] 的解析机制,到借用反序列化如何在编译期证明生命周期安全。
这是《Rust 源码之道》本系列的第四卷。读完本专栏,你不只学会了 Serde——你掌握了 Rust 过程宏这门"元能力"。再看 Axum 的 #[derive(FromRequest)]、sqlx 的 query!、clap 的 #[derive(Parser)]、tokio 的 #[tokio::main],你会发现它们共享同一套工程范式。
flowchart TB
D["#[derive(Serialize, Deserialize)]"] --> PM
subgraph PM [过程宏 · 编译期运行的 Rust 函数]
direction LR
TS[TokenStream] --> SYN[syn<br/>解析成 AST] --> ATTR["属性系统<br/>#[serde(...)]"]
ATTR --> QT[quote<br/>生成代码]
end
QT --> CG["codegen<br/>serialize_body 五路分派"]
CG --> IMPL[你的类型的 impl]
IMPL --> DM
subgraph DM [Data Model · 把 M×N 降成 M+N]
direction LR
SER["Serializer trait<br/>30 个方法 · self 是 move"] --- DE[Deserializer + Visitor]
end
DM --> FMT[JSON · YAML · TOML<br/>Bincode · MessagePack]
Serde 真正的设计眼光在中间那一格。 M 种数据结构 × N 种格式本来是组合爆炸,Data Model 把它降成 M+N —— 每个类型只需实现一次 Serialize,每个格式只需实现一次 Serializer。这个抽象值多少,看一眼 Rust 生态里格式库的数量就知道了。
读完你能做到什么
- 理解 Data Model 这个中间层。 29 种原语的划分依据、为什么它能把组合爆炸降维、边界在哪(第2章 Data Model)。
- 读懂两个核心 trait 的设计取舍。 Serializer 的 30 个方法怎么组织、
self为什么是 move 语义、默认实现的性能兜底;以及 Visitor 模式为什么是反序列化的唯一正解(第3章 Serializer、第4章 Deserializer 与 Visitor)。 - 自己写一个 derive 宏。 宏的三种形态、过程宏的运行时机、
syn解析 AST、quote的 quasi-quoting 与#var插值、ToTokens、#(...)*重复语法,然后动手写出第一个 derive(第5章 宏系统全景、第6章 TokenStream、第7章 syn、第8章 quote、第9章 第一个 derive)。 - 看懂 Serde 自己的代码生成。 derive 的整体架构、属性系统怎么解析、
expand_derive_serialize的全貌、serialize_body的五路分派,以及各种 struct/enum 形态各自生成什么(第10章 derive 架构、第11章 属性系统、第12章 序列化 codegen、第13章 反序列化 codegen)。 - 处理 enum 与借用这两个难点。 四种 enum 标签表示(externally / internally / adjacently / untagged)各自的取舍,以及借用反序列化如何在编译期证明生命周期安全(第14章 enum 标签、第15章 借用反序列化)。
- 用好高级属性并读懂一个真实格式实现。 进阶属性组合,以及 serde_json 是怎么落地这套抽象的(第16章 高级属性、第17章 serde_json、第18章 设计哲学)。
适合谁读
- Rust 开发者:想从"会用 derive"进阶到"会写 derive"
- 框架/库作者:准备为自己的项目设计 derive 宏或属性宏
- 系统程序员:想深入理解"零成本抽象"在元编程层面如何落地
- Serde 重度使用者:被
#[serde(with)]、flatten、untagged等高级特性卡住,想从源码层面彻底理解
前置阅读:本专栏假设读者已经能写 Rust 代码,理解 trait、泛型、生命周期的基本概念。如果对这些机制的编译期实现尚有疑问,建议先读《Rust 编译器》。本专栏不依赖 Tokio 相关知识,可独立阅读。
目录
第一部分:开篇
第二部分:Data Model 与 trait 抽象
- 第2章 Serde Data Model:29 种原语的设计哲学
- 第3章 Serializer trait:序列化端的 O(M) 抽象
- 第4章 Deserializer 与 Visitor:反序列化的反向控制
第三部分:过程宏工程学
- 第5章 Rust 宏系统全景:声明宏 vs 过程宏 vs derive 宏
- 第6章 TokenStream 与 proc_macro2:编译期的字节流
- 第7章 syn:把 TokenStream 解析成 AST
- 第8章 quote:用模板反向生成代码
- 第9章 从零实现 #[derive(Hello)]:过程宏工程全流程
第四部分:serde_derive 源码剖析
- 第10章 serde_derive 架构总览:从 AST 到 impl 块
- 第11章 属性宏系统:#[serde(...)] 的解析机制
- 第12章 Serialize 代码生成:结构体与枚举
- 第13章 Deserialize 代码生成:Visitor 的自动构造
- 第14章 Enum 的四种 tag 策略
第五部分:高阶主题
第六部分:生态与总结
源码版本
本专栏所有源码引用均基于以下版本(2026 年 4 月 20 日锁定):
| Crate | 版本 | Git Commit |
|---|---|---|
| serde | 1.0.228 | fa7da4a93567ed347ad0735c28e439fca688ef26 |
| serde_json | 1.0.149 | dc8003a88e7142529cf4a7429c4778af31dadf50 |
| syn | 2.0.117 | e027fef2adcd6588fa8097c63dc916778d1aa11b |
| quote | 1.0.45 | ba07807af385afeff97a5e75186f7a92eb629a79 |
| proc-macro2 | 1.0.107 | ed8a5497669cd63db33bf24646f261b012bbbc4a |
读者可通过以下命令获取与本专栏完全一致的源码:
git clone https://github.com/serde-rs/serde.git
cd serde && git checkout fa7da4a
git clone https://github.com/serde-rs/json.git serde_json
cd serde_json && git checkout dc8003a
git clone https://github.com/dtolnay/syn.git
cd syn && git checkout e027fef
git clone https://github.com/dtolnay/quote.git
cd quote && git checkout ba07807
正文每一段源码引用都会标注文件路径和行号,读者可在对应版本的代码中逐行验证。
版权声明
本专栏内容为 杨艺韬 版权所有,保留一切权利。未经书面许可,不得全文或大段转载、改编、翻译,或用于任何商业用途(含以本专栏内容训练模型、生成衍生课程或商品)。
欢迎分享本专栏的链接。引用少量内容用于评论、教学或研究时,请署名 杨艺韬 并附上原文链接。