Serde 元编程

前言

作者 杨艺韬 · 3,740 字

前言

写给准备翻开这本书的你

在 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 基础。阅读前请确认你对以下概念心中有数:

必须掌握:

最好掌握(不掌握也能读,但会有些吃力):

完全不需要:

本书的阅读路径

本书共 19 章(含前言),按四条主线展开。章节之间的依赖关系如下:

graph TD
    P[00 前言]
    C1[01 为什么需要 Serde]

    C2[02 Data Model]
    C3[03 Serializer trait]
    C4[04 Deserializer & Visitor]

    C5[05 宏系统全景]
    C6[06 TokenStream]
    C7[07 syn]
    C8[08 quote]
    C9[09 第一个 derive 宏]

    C10[10 serde_derive 架构]
    C11[11 属性宏系统]
    C12[12 Serialize 生成]
    C13[13 Deserialize 生成]
    C14[14 Enum tag 策略]

    C15[15 借用反序列化]
    C16[16 高级属性]

    C17[17 serde_json]
    C18[18 设计哲学]

    P --> C1
    C1 --> C2
    C2 --> C3
    C2 --> C4
    C3 --> C10
    C4 --> C10

    C1 --> C5
    C5 --> C6 --> C7 --> C8 --> C9
    C9 --> C10

    C10 --> C11
    C10 --> C12
    C10 --> C13
    C13 --> C14

    C13 --> C15
    C11 --> C16

    C4 --> C17
    C12 --> C17
    C13 --> C17

    C17 --> C18

三种阅读路径:

  1. 顺序精读(推荐首次阅读):从前言到第 18 章一章不落。每一章都为后续章节埋下伏笔,跳读会错过很多"原来如此"的瞬间。预计阅读时间:专注阅读 25-35 小时,包含边读边动手验证代码。

  2. 元编程主线:只想学过程宏,跳过 Data Model 细节。路径:前言 → 第 1 章 → 第 5-9 章 → 第 10-13 章(只看代码生成部分)。预计 10-15 小时。

  3. 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 过程宏代码生成器

获取与本书完全一致的源码:

# 建议创建一个统一目录存放源码
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

阅读源码的建议工具:

如果你的版本和本书不符怎么办? Serde 1.x 是一个非常稳定的 API——1.0.228 和 1.0.200 之间内部实现几乎没有变化。即使你用的是 1.0.250 之类的未来版本,99% 的代码结构依然对得上。如果遇到对不上的情况,以 git blame 为准——查一下那段代码是什么时候改的,通常能看到背后的 issue 或 PR 链接,读完就能理解变化。

一些叙事约定

为了保证阅读体验一致,本书做如下约定:

代码引用格式

// 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 的根目录开始。行号基于本书指定的版本,其他版本可能偏移。

引用简写:为了行文流畅,书中偶尔用以下简写:

Mermaid 图:本书大量使用流程图、序列图、状态图可视化复杂关系。如果你在离线环境阅读 Markdown,建议用支持 Mermaid 渲染的阅读器(如 Typora、Obsidian、VS Code 的 Markdown Preview Enhanced)。

"设计意图"方框:书中凡是讨论"为什么这样设计而不是那样"的段落,会用 > **设计意图**:... 引用块标注。如果时间有限、只想速读,盯着这些方框读也能获得大部分价值。

致谢与反馈

Serde 的存在本身就是对 Rust 生态最大的贡献之一。感谢 dtolnayoli-obk 和所有 serde-rs 组织的贡献者。没有他们十年如一日的工程投入,就没有今天 Rust 生态的繁荣,也没有本书。

本书写作过程中大量参考了 Serde 官方文档、RFC、issue 讨论、以及 dtolnay 本人在 Rust 论坛和 Twitter 上的技术分享。每一处实质引用都在对应章节末尾列出。

如果你在阅读中发现任何错误——无论是代码引用的行号不对、Mermaid 图有歧义、还是某处设计意图分析有误——请通过以下渠道反馈:

我会在每个季度结束时合并错误反馈、更新源码版本(如有重要变更),并在本书的"版本变更日志"中记录。


下一章我们从一个看似简单的问题开始:为什么需要 Serde? 你会发现这个问题的答案,一点都不简单。