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 个 serialize_* 方法 · self 是 move"] --- DE[Deserializer + Visitor]
end
DM --> FMT[JSON · YAML · TOML<br/>Bincode · MessagePack]
Serde 真正的设计眼光在中间那一格。 M 种数据结构 × N 种格式本来是组合爆炸,Data Model 把它降成 M+N —— 每个类型只需实现一次 Serialize / Deserialize,每个格式只需实现一次 Serializer / Deserializer。这个抽象值多少,看一眼 Rust 生态里格式库的数量就知道了。
读完你能做到什么
- 理解 Data Model 这个中间层。 29 种原语的划分依据、为什么它能把组合爆炸降维、边界在哪(第2章 Data Model)。
- 读懂两个核心 trait 的设计取舍。 Serializer 的 30 个
serialize_*方法怎么组织、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(N) 抽象
- 第4章 Deserializer 与 Visitor:反序列化的反向控制
第三部分:过程宏工程学
- 第5章 Rust 宏系统全景:声明宏、过程宏与 derive 宏
- 第6章 TokenStream 与 proc_macro2:编译期的"字节流"
- 第7章 syn:把 TokenStream 解析成 AST
- 第8章 quote:用模板反向生成 TokenStream
- 第9章 从零实现第一个 derive 宏:#[derive(Builder)]
第四部分:serde_derive 源码剖析
- 第10章 serde_derive 架构总览:从 AST 到 impl 块
- 第11章 属性宏系统:#[serde(...)] 的解析机制
- 第12章 Serialize 代码生成:结构体与枚举
- 第13章 Deserialize 代码生成:Visitor 的自动构造
- 第14章 Enum 的四种 tag 策略
第五部分:高阶主题
第六部分:生态与总结
源码版本
本专栏所有源码引用均基于以下版本(2026 年 4 月 20 日锁定;proc-macro2 与 rust-lang/rust 两行为后来补入,其版本发布于锁定日之后):
| 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 |
| sqlx | 0.8.6 | bab1b022bd56a64f9a08b46b36b97c5cff19d77e(对照用:第 5 / 7 / 8 章拿 sqlx::query! 作对照,不属于 serde 生态) |
| rust-lang/rust | 1.96.0 | tag 1.96.0(第 5–8 章引到 rustc 源码,如第 5 章讲 fragment specifier 时的 compiler/rustc_ast/src/token.rs 与 compiler/rustc_feature/src/unstable.rs) |
表中版本号取各仓库 Cargo.toml。其中 serde、serde_json、syn、quote 的 commit 并非 release tag 本身,而是 tag 之后的主线提交(分别领先 31 / 7 / 16 / 1 个提交),与 crates.io 上的同号发布版可能有个别行号出入;proc-macro2 的 commit 就是 1.0.107 的发布提交。
读者可通过以下命令获取与本专栏完全一致的源码:
git clone https://github.com/serde-rs/serde.git
git -C serde checkout fa7da4a
git clone https://github.com/serde-rs/json.git serde_json
git -C serde_json checkout dc8003a
git clone https://github.com/dtolnay/syn.git
git -C syn checkout e027fef
git clone https://github.com/dtolnay/quote.git
git -C quote checkout ba07807
git clone https://github.com/dtolnay/proc-macro2.git
git -C proc-macro2 checkout ed8a549
git clone https://github.com/launchbadge/sqlx.git
git -C sqlx checkout bab1b02
正文每一段源码引用都会标注文件路径和行号,读者可在对应版本的代码中逐行验证。
版权声明
本专栏内容为 杨艺韬 版权所有,保留一切权利。未经书面许可,不得全文或大段转载、改编、翻译,或用于任何商业用途(含以本专栏内容训练模型、生成衍生课程或商品)。
欢迎分享本专栏的链接。引用少量内容用于评论、教学或研究时,请署名 杨艺韬 并附上原文链接。