Appearance
前言
写给准备翻开这本书的你
在 Rust 的世界里,#[derive(Serialize, Deserialize)] 是一行几乎没有存在感的代码。它朴素得像一个标点符号——你写下它,编译器接管一切,你的结构体突然就会在 JSON、YAML、TOML、Bincode、MessagePack、Postcard、CBOR 之间自由流转。
如果只是使用者,你可能永远不会去想这行代码背后发生了什么。但只要你在 Rust 生态里多走几步,就会发现 Serde 的影子无处不在。Axum 用同样的机制生成 FromRequest;sqlx 用同样的工具把 SQL 字面量翻译成类型安全的查询;clap 用同样的模式把 struct 转换成命令行参数解析器;tokio 的 #[tokio::main] 把 async fn 包裹成运行时入口。所有这些"看起来像魔法"的 derive 宏,共享着一套完全相同的工程范式——而 Serde,是这套范式最早、最成熟、最经得起推敲的范本。
这本书的目的不是教你用 Serde。如果只是想会用,读官方文档半小时就够了。这本书要做的是把 Serde 当作一本教材,通过一行 #[derive(Serialize)] 的完整解剖过程,让你掌握 Rust 这门语言最强大也最晦涩的能力——在编译期读懂、改写、生成代码的元编程能力。
读完本书,你不再只是"Serde 用户"。你会变成一个能设计 derive 宏、能阅读 serde_derive 源码、能在自己的项目里落地零成本抽象的 Rust 工程师。
这本书不是什么
在讨论本书"是什么"之前,先把"不是什么"说清楚,避免你带着错误的期待翻到第 30 页才发现读错了书。
这本书不是 Serde 用户手册。 #[serde(rename = "foo")] 怎么写、#[serde(default)] 的默认值从哪来、Option<T> 在 JSON 里如何序列化——这些问题在 serde.rs 官方文档里都有答案,本书不会重复。唯一会出现用法介绍的地方,是当我们需要用一个具体例子来切入源码分析时——用法是门票,不是目的地。
这本书不是 JSON 解析教程。 serde_json 会作为第 17 章的案例出现,但那一章讨论的不是"如何解析 JSON",而是"一个手写的 JSON 解析器如何把自己嵌入 Serde 的 Data Model"。如果你只想写一个 JSON 解析器,应该去读 nom 的源码或 Crafting Interpreters。
这本书不是 Rust 入门书。 我假设你能写 Rust 代码,知道什么是 trait、泛型、生命周期,用过 Result 和 ?,至少见过 Box<dyn Trait>。如果你在这些概念上还有疑问,请先读《Rust 程序设计语言》(The Rust Programming Language) 或丛书卷一《Rust 编译器》。
这本书不是"怎么写过程宏"速成课。 我们会花四章讲过程宏,但不是为了让你十分钟上手——那类教程网上很多。我们讲过程宏是为了读懂 serde_derive 的源码。你会学到工具用法,但更重要的是学到工程权衡:什么时候用 syn::parse,什么时候用 TokenStream::extend;为什么 quote! 用 #var 而不是 {var};Span 在错误信息里起什么作用。
这本书是什么
这本书是一本工程实录。 Serde 从 2016 年发布以来,经过近十年打磨,每一个设计选择都经历过真实生产环境的考验。这些选择不是写在论文里的理想化模型,而是一次次权衡、妥协、重构之后沉淀下来的工程智慧。本书的每一章都会回答两个问题:当时面临什么问题?为什么这样解决而不是那样? 代码是答案,但设计意图才是真正值得学习的东西。
这本书是一张 Rust 元编程的地图。 过程宏、声明宏、derive 宏、属性宏、TokenStream、Span、syn::parse、quote!、proc_macro2——这些名词零散地散落在 Rust 文档里,但从来没有被系统地组织成一个整体。本书第 5-9 章会提供一张完整地图,告诉你每个工具的定位、边界和适用场景。读完这五章,你会拥有独立写 derive 宏的能力,而不再只能 copy-paste 官方示例。
这本书是一份零成本抽象的案例研究。 "零成本抽象"这个词被 Rust 社区说烂了,但真正能用具体例子讲清楚它的书不多。Serde 是一个完美案例——它同时用了**过程宏(编译期代码生成)、trait(静态分发)、泛型(单态化)、Visitor 模式(零分配反序列化)、生命周期(借用零拷贝)**这五种抽象手段,而最终产物的性能和手写代码没有区别。本书会用性能数据和汇编产物证明这一点——不是"听说很快",而是"看,编译器真的把这一坨抽象消解成了和手写一样的机器码"。
读者画像
如果你属于下面任何一类,这本书是为你写的:
Rust 重度使用者,想从"会用"进阶到"会造"。 你每天写 Rust 业务代码,
#[derive(Serialize, Deserialize)]按得很熟,但从没想过自己写一个#[derive(MyTrait)]。你隐约觉得那是"库作者才会做的事",离自己很远。读完本书,你会发现它不仅不远,而且一旦掌握,你会在自己的项目里不断发现"这里可以写个 derive"的机会——日志埋点、数据库映射、API 契约校验、配置校验,都能被 derive 宏简化。开源框架或内部基础设施作者。 你在写自己的 Web 框架、ORM、RPC 框架、配置系统,需要给用户提供"拿来就用"的 derive 宏体验。你读过一些
proc_macro教程,但一上手就被syn::DeriveInput的嵌套结构劝退了。本书不是从抽象文档讲起,而是带你读一遍 Serde 这个"工业级模板"——你会学到怎么组织代码、怎么处理错误、怎么设计属性宏 API,而这些都是教程从来不讲的。性能敏感的系统程序员。 你正在用 Rust 写高性能网络服务、数据库、ML 推理引擎,序列化开销是热点。你想知道
serde_json::from_slice到底做了什么、能不能更快、什么时候应该手写而不是 derive。本书第 15、17、18 章会给你答案——以及更重要的,会让你有能力自己判断答案。Serde 高阶用法的攻关者。 你被
#[serde(with = "...")]、#[serde(remote)]、#[serde(flatten)]、untaggedenum 卡住过。文档说"你可以这样用",但没说"它为什么能这样用"。一旦业务场景稍微偏离教科书例子,你就不知道怎么办。第 14 章和第 16 章会把这些特性的实现机理彻底拆开——理解了实现,你就能准确预判边界。对编程语言设计感兴趣的学习者。 Serde 是 Rust 社区少数几个在"语言设计"意义上值得研究的工程——它证明了过程宏系统不只是"节省重复代码"的工具,而能承载整个生态的抽象边界。如果你对编译期元编程、类型驱动设计、零成本抽象感兴趣,本书会给你大量"为什么这样设计"的一手资料。
前置知识
本书不会重复 Rust 基础。阅读前请确认你对以下概念心中有数:
必须掌握:
- trait 与泛型:知道
impl Trait for Type和fn foo<T: Trait>(x: T)各自的含义与编译期行为。第 3、4 章所有抽象都建立在 trait 泛型之上。 - 生命周期:能看懂
fn foo<'a>(x: &'a str) -> &'a str,理解生命周期是"借用关系"而非"时间段"。第 15 章会深入讨论生命周期在反序列化中的精妙用法。 Result与?:第 3、4 章所有 trait 方法都返回Result,错误传播是 Serde 的核心议题之一。- 枚举与模式匹配:
match、if let、enum的基本用法。Serde 内部大量使用 enum 表达状态。
最好掌握(不掌握也能读,但会有些吃力):
- Rust 宏基础:
macro_rules!写过至少一个。第 5 章会对比声明宏和过程宏,有过声明宏经验会更快建立直觉。 - Rust 的 HIR/MIR 概念:知道 Rust 编译器有多层中间表示。第 6 章讨论
TokenStream时会类比这个层次结构。 - 一点编译器常识:知道 AST、parser、code generation 大致指什么。过程宏本质上是一个小编译器。
完全不需要:
- Tokio 或 async/await。本书不涉及异步。
- LLVM、汇编、unsafe。我们只在第 18 章讨论性能时用到一点 cargo-asm 输出,但不要求你能手写汇编。
- 其他序列化库的经验。protobuf、Thrift、Cap'n Proto 会作为对比对象在第 1 章出现,但不需要你用过它们。
本书的阅读路径
本书共 19 章(含前言),按四条主线展开。章节之间的依赖关系如下:
三种阅读路径:
顺序精读(推荐首次阅读):从前言到第 18 章一章不落。每一章都为后续章节埋下伏笔,跳读会错过很多"原来如此"的瞬间。预计阅读时间:专注阅读 25-35 小时,包含边读边动手验证代码。
元编程主线:只想学过程宏,跳过 Data Model 细节。路径:前言 → 第 1 章 → 第 5-9 章 → 第 10-13 章(只看代码生成部分)。预计 10-15 小时。
Serde 深度调优主线:已经熟悉过程宏,专注于 Serde 本身。路径:前言 → 第 1-4 章 → 第 10-16 章 → 第 17 章。预计 15-20 小时。
每一章末尾有"延伸阅读"和"动手实验"两个小节。延伸阅读指向官方 RFC、issue 讨论、相关项目源码;动手实验是可以在本地复现的小练习,建议至少完成 70%——Rust 元编程是典型的"读懂不等于会写"的技能,必须动手。
源码版本与获取
质量红线:本书所有代码引用都来自真实源码,标注了文件路径和行号,读者可以逐行验证。这不是一句客套话——我在写作过程中发现,网上大量 Serde 教程的代码和官方源码完全对不上(版本不符、凭记忆写的"类似代码")。这种行为会彻底误导读者。本书承诺:每一段引用的代码都能在指定版本的源码里精确找到。
本书基于以下版本(2026 年 4 月 20 日锁定):
| Crate | 版本 | Git Commit | 说明 |
|---|---|---|---|
| serde | 1.0.228 | fa7da4a | 核心 trait 定义和标准库实现 |
| serde_json | 1.0.149 | dc8003a | 第 17 章案例研究 |
| syn | 2.0.117 | e027fef | 过程宏 AST 解析器 |
| quote | 1.0.45 | ba07807 | 过程宏代码生成器 |
获取与本书完全一致的源码:
bash
# 建议创建一个统一目录存放源码
mkdir -p ~/serde-book-sources && cd ~/serde-book-sources
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阅读源码的建议工具:
- IDE:VS Code + rust-analyzer,或 RustRover。阅读 serde_derive 源码时,跳转定义 (
Go to Definition) 的能力至关重要。 - 命令行:
rg(ripgrep)用于全局搜索。书中每个源码引用都标注了文件:行号,你可以用rg -n快速定位。 - 宏展开器:
cargo expand(cargo install cargo-expand)。这是本书最重要的一个工具——它能让你看到#[derive(Serialize)]到底展开成了什么代码。第 9 章开始我们会频繁使用它。
如果你的版本和本书不符怎么办? Serde 1.x 是一个非常稳定的 API——1.0.228 和 1.0.200 之间内部实现几乎没有变化。即使你用的是 1.0.250 之类的未来版本,99% 的代码结构依然对得上。如果遇到对不上的情况,以 git blame 为准——查一下那段代码是什么时候改的,通常能看到背后的 issue 或 PR 链接,读完就能理解变化。
一些叙事约定
为了保证阅读体验一致,本书做如下约定:
代码引用格式:
rust
// serde/serde_core/src/ser/mod.rs:420
pub trait Serialize {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer;
}第一行注释给出文件相对路径和行号。路径从上面表格中某个 Crate 的根目录开始。行号基于本书指定的版本,其他版本可能偏移。
引用简写:为了行文流畅,书中偶尔用以下简写:
- "Serializer":大写 S 开头,指 trait 本身(在
serde_core::ser::Serializer) - "serializer":小写开头,指 trait 的某个实现或 trait 对象
- "Data Model":首字母大写,特指 Serde 定义的 29 种原语抽象(第 2 章详述)
Mermaid 图:本书大量使用流程图、序列图、状态图可视化复杂关系。如果你在离线环境阅读 Markdown,建议用支持 Mermaid 渲染的阅读器(如 Typora、Obsidian、VS Code 的 Markdown Preview Enhanced)。
"设计意图"方框:书中凡是讨论"为什么这样设计而不是那样"的段落,会用 > **设计意图**:... 引用块标注。如果时间有限、只想速读,盯着这些方框读也能获得大部分价值。
致谢与反馈
Serde 的存在本身就是对 Rust 生态最大的贡献之一。感谢 dtolnay、oli-obk 和所有 serde-rs 组织的贡献者。没有他们十年如一日的工程投入,就没有今天 Rust 生态的繁荣,也没有本书。
本书写作过程中大量参考了 Serde 官方文档、RFC、issue 讨论、以及 dtolnay 本人在 Rust 论坛和 Twitter 上的技术分享。每一处实质引用都在对应章节末尾列出。
如果你在阅读中发现任何错误——无论是代码引用的行号不对、Mermaid 图有歧义、还是某处设计意图分析有误——请通过以下渠道反馈:
- 本书托管仓库的 issue 区
- 邮件:yangyitao3000@gmail.com
我会在每个季度结束时合并错误反馈、更新源码版本(如有重要变更),并在本书的"版本变更日志"中记录。
下一章我们从一个看似简单的问题开始:为什么需要 Serde? 你会发现这个问题的答案,一点都不简单。