Rust编译器原理-前言

《Rust 编译器原理》完整目录

前言

写作动机

每一个 Rust 开发者都经历过这样的时刻:编译器报了一个 lifetime 错误,你盯着那几行代码看了十分钟,试了各种写法,终于编译通过了------但你不知道为什么。

你学会了"和编译器搏斗"。你知道在这里加一个 'a,在那里加一个 clone(),在某些地方用 Arc<Mutex<T>>。你的代码能编译、能运行、性能不错。但有一个问题始终萦绕在脑海:

编译器到底在做什么?

  • 所有权的 move 语义,在编译器内部对应的是什么操作?
  • 借用检查器是怎么判断一个引用会不会悬垂的?它用的是什么算法?
  • 泛型函数说是"零成本抽象",编译器具体做了什么来消除运行时开销?
  • async fn 明明返回的是一个 impl Future,为什么编译器能在编译期确定它的大小?
  • dyn Trait 的 vtable 里到底存了什么?一个 trait object 在内存里长什么样?

这些问题,Rust 官方文档不会告诉你,《Rust 程序设计语言》不会告诉你,绝大多数 Rust 教程也不会告诉你。因为它们教的是"怎么用 Rust",而不是"Rust 怎么工作"。

这本书填补的就是这个空白。

这本书讲什么

本书不教 Rust 语法。如果你还不会写 Rust,请先阅读 The Rust Programming Language

本书讲的是 Rust 编译器的内部行为------从你写下一行 Rust 代码到最终生成机器码的过程中,编译器对每一个核心语言特性做了什么。

具体来说,本书回答以下问题:

所有权与内存

  • move 语义在 MIR(中间表示)中是如何表达的?Copy 和 Move 的区别在编译产物中长什么样?
  • 借用检查器使用的 NLL(Non-Lexical Lifetimes)算法是怎么工作的?它如何构建控制流图、如何计算借用的活跃区间?
  • 编译器是怎么推导 lifetime 的?什么时候需要你手动标注、什么时候不需要?
  • structenumunion 在内存中是怎么排列的?Niche 优化是怎么让 Option<&T>&T 一样大的?

类型系统与泛型

  • 单态化(monomorphization)的展开过程是什么?一个 fn foo<T>(x: T) 被三种类型调用后,编译器生成了几份代码?
  • 静态分发和动态分发的区别在 LLVM IR 层面具体是什么?
  • dyn Trait 的 fat pointer 里存了什么?vtable 的结构是怎样的?

异步

  • async fn 被编译器展开成了什么?生成的状态机有几个状态、每个状态存了哪些字段?
  • Pin 解决的"自引用结构"问题到底是什么?为什么 &mut self 不够用?
  • Waker 的注册和唤醒机制是怎么和 Tokio 的 reactor 配合的?

闭包、unsafe 与 FFI

  • 闭包捕获变量时,编译器生成的匿名 struct 长什么样?FnFnMutFnOnce 的区别在编译产物中如何体现?
  • unsafe 块的边界在编译器实现中是如何划定的?编译器在 unsafe 块内关闭了哪些检查?
  • 跨 FFI 边界调用 C 函数时,参数是怎么传递的?ABI 约定如何影响内存布局?

宏与元编程

  • 声明宏(macro_rules!)和过程宏(proc_macro)的展开发生在编译的哪个阶段?
  • 过程宏拿到的 TokenStream 和最终的 AST 之间经历了什么?

编译器后端

  • MIR 层做了哪些优化?常量传播、死代码消除、内联是怎么工作的?
  • MIR 是怎么被翻译成 LLVM IR 的?Rust 的类型信息在 LLVM IR 中还存在吗?
  • 增量编译是怎么决定"哪些代码需要重新编译"的?

本书的方法论:对照法

本书的核心方法是**"你写的 vs 编译器做的"对照**。

每一个核心概念,我们都会先给出一段简洁的 Rust 代码,然后展示编译器对这段代码的处理结果------可能是 MIR、可能是 LLVM IR、可能是内存布局图、可能是状态机的展开形式。

例如,讲 async/await 时:

rust 复制代码
// 你写的
async fn fetch(url: &str) -> String {
    let resp = http_get(url).await;
    let body = resp.text().await;
    body.to_uppercase()
}
rust 复制代码
// 编译器生成的(简化)
enum FetchFuture<'a> {
    Start { url: &'a str },
    WaitingHttpGet { url: &'a str, fut: HttpGetFuture<'a> },
    WaitingText { fut: TextFuture },
    Done,
}

impl<'a> Future for FetchFuture<'a> {
    type Output = String;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<String> {
        // 状态转移逻辑...
    }
}

这种对照让你同时看到"抽象"和"实现"。你不需要去猜编译器做了什么------你直接看到了。

书中所有的 MIR 和 LLVM IR 输出都是通过以下命令实际生成的,不是凭空编造:

bash 复制代码
# 查看 MIR
cargo rustc -- --emit=mir

# 查看 LLVM IR
cargo rustc -- --emit=llvm-ir

# 查看内存布局
cargo rustc -- -Zprint-type-sizes  # nightly

# 查看编译器的借用检查分析
RUSTC_LOG=rustc_borrowck cargo rustc 2>&1

你可以用同样的命令在自己的代码上验证本书的每一个结论。

本书的组织

全书分为八个部分,按照 Rust 语言特性从基础到高级组织:

第一部分:编译全景(第 1 章)------Rust 代码从源文件到可执行文件的完整旅程:解析 → HIR → MIR → LLVM IR → 机器码。这一章建立全局地图,后续章节在这张地图上逐个区域深入。

第二部分:所有权与内存(第 2-5 章)------Rust 最核心的创新。我们会深入 MIR 看 move/copy 的实现,拆解 NLL 借用检查算法,理解 lifetime 推导的规则,以及编译器如何决定一个类型在内存中的排列方式。

第三部分:类型系统与泛型(第 6-8 章)------零成本抽象的真正含义。单态化如何消除泛型开销,trait 的两种分发策略在底层有什么区别,vtable 的结构和代价。

第四部分:异步机制(第 9-10 章)------Rust 异步模型的编译器实现。async/await 的状态机展开、Pin 的必要性、Waker 的协作机制。这两章是后续阅读 Tokio 源码的前置知识。

第五部分:闭包、unsafe 与 FFI(第 11-13 章)------编译器的"柔软面"和"硬边界"。闭包的匿名类型生成、unsafe 的精确边界、跨 ABI 的函数调用。

第六部分:宏与元编程(第 14 章)------编译前的编译。声明宏的模式匹配和过程宏的 token 流处理。

第七部分:编译器后端与优化(第 15-17 章)------从 MIR 到机器码的最后一程。MIR 层的优化 pass、LLVM IR 的生成与优化、增量编译的缓存策略。

第八部分:设计哲学(第 18 章)------回到"为什么"。Rust 的核心设计决策背后的哲学:编译期 vs 运行时、安全 vs 灵活、零成本 vs 易用。以及这些设计模式如何迁移到其他系统的设计中。

本书读者

你需要:至少写过几千行 Rust 代码,了解所有权、借用、trait、async/await 的基本用法。

你不需要:编译器开发经验、LLVM 知识、计算机科学学位。本书会从零开始解释每一个编译器概念。

你将获得

  • 遇到 lifetime 错误时,知道编译器的判断逻辑,能直接写出正确的代码而不是靠试
  • dyn Trait 和泛型之间做选择时,知道两者在底层的精确代价
  • 读 Tokio、Axum、TiKV 等项目源码时,不会在 Pin<Box<dyn Future>> 上卡住
  • 理解"零成本抽象"不是营销口号,而是编译器用单态化、内联、LLVM 优化实现的具体工程

与后续书籍的关系

本书是"Rust 后端系列"的第一本。后续书籍将深入 Tokio、Axum、Pingora、MeiliSearch、TiKV 等 Rust 基础设施项目的源码。

在那些书中,你会频繁遇到本书讲解过的机制:

  • 读 Tokio 时,第 9-10 章(async 状态机、Pin/Waker)是基础
  • 读 Axum 时,第 6-8 章(单态化、trait 分发、trait object)是基础
  • 读 TiKV 时,第 12-13 章(unsafe、FFI)是基础
  • 读任何 Rust 项目时,第 2-5 章(所有权、借用检查、内存布局)是基础

把这本书当作整个系列的"解码器"------先装好它,后面的书读起来会顺畅得多。

源码版本与验证

本书基于 Rust 1.88 stable (2026 年 4 月)分析。编译器源码引用来自 rust-lang/rust 仓库。

书中所有编译器输出(MIR、LLVM IR、类型布局)均可通过标准工具链复现:

bash 复制代码
# 安装 nightly(用于部分诊断输出)
rustup install nightly

# 查看 MIR
cargo +nightly rustc -- -Zunpretty=mir

# 查看 LLVM IR
cargo rustc --release -- --emit=llvm-ir

# 查看类型大小和布局
cargo +nightly rustc -- -Zprint-type-sizes

不要只读------动手验证。把书中的示例代码粘贴到你的项目里,用上面的命令看编译器的实际输出,和书中的分析对照。这是最快的学习方式。

相关推荐
米丘8 小时前
Rust 初了解
rust
古城小栈8 小时前
rustup 命令工具,掌控 Rust 开发环境
开发语言·后端·rust
咸甜适中11 小时前
rust语言待办事项小实例完整代码(axum+sqlx+sqlite+自定义错误)
rust·sqlite·axum·sqlx
Rust研习社11 小时前
深入 Rust 引用计数智能指针:Rc 与 Arc 从入门到实战
开发语言·后端·rust
@atweiwei13 小时前
LangChainRust:用 Rust 构建高性能 LLM 应用的完整指南
开发语言·人工智能·ai·rust·大模型·llm·agent
浪客川14 小时前
【百例RUST - 006】一文理解所有权和切片
开发语言·后端·rust
Rust研习社14 小时前
Rust 是如何判断对象是否相等的?一起来聊一聊 PartialEq 与 Eq
后端·rust·编程语言
古城小栈14 小时前
Rust在当下AI领域的用武之地:从底层加速到上层应用全解析
开发语言·人工智能·rust
卜夋14 小时前
Rust 所有权概念
后端·rust