Programming Rust 3e(Early Release)— 全书拆解
Programming Rust 3e(Early Release)— 全书拆解
30 秒导读: 这是 O'Reilly 的 Rust 权威教材《Programming Rust》第三版,Early Release 抢先版—— 正文目前只放出第 1–5 章,但这五章恰好是全书最硬的核心:Rust 为什么敢不要垃圾回收。 它的答案是:给指针的使用立一套编译期查得动的规则(谁拥有、谁能改、能活多久), 代价是你写代码的方式要改,回报是悬垂指针、重复释放、数据竞争这一整类 bug 在编译期就被拒绝。 本组文档按我们的讲义口径重写这五章,读完不必看原书。
1. 先交代清楚:这是谁在什么时候写的
| 作者 | Jim Blandy、Jason Orendorff、Leonora F. S. Tindall;第一作者 Blandy 是 Rust 核心团队的早期成员 |
| 版本 | 第三版,First Early Release(成书过程中抢先放出的未编辑稿),按 Rust 2024 edition 更新,书内工具链示例是 Rust 1.85(2025 年初)1 |
| 性质 | 教科书,默认读者已经会一门系统语言(C/C++ 经验最佳),不是零基础入门书 |
| 完整性 | 正文只到第 5 章为止;全书内部交叉引用大量挂着「[Link to Come]」占位符——读的时候要意识到后面还有十几章没出来 |
两件事必须先说:
第一,作者写作时的自我定位。 第一章开篇就承认:Rust 没有那种能印在封面上炫耀的单一特性, 它的性格在于所有部分为同一个目标协同工作——「安全的、高性能的系统编程」2。 所以这本书的讲法也不是「特性清单」,而是先带你写三个完整小程序,再把每个部分拆给你看。
第二,利益相关方——这本书是 Rust 的「辩护书」。 作者们是 Rust 社区的内部人, 书里那句「Rust 的激进赌注」是作者自己的措辞,他们明说自己是「看多 Rust」的一方3。 论证本身站得住,但读的时候要分清哪些是机制事实、哪些是立场。
2. 全书一条主线(读完这一节,你就懂了这本书)
这本书回答一个问题:「内存释放要及时」和「指针永不悬垂」看起来互相矛盾,Rust 怎么两个都要? 下面把已放出章节的推理链完整走一遍——只读这一节,你也能把这本书讲给别人听。
① 矛盾本身:两派语言各放弃了一样东西
内存管理想要的两件事:内存在我选定的时间被释放;绝不在释放之后还用指向它的指针。 问题在于:指针还在的时候把内存放了,指针就悬垂了——两件事天然打架。
于是所有主流语言二选一:要么「安全优先」——装垃圾回收,内存等到没人指向才放 (释放时机不归你管);要么「控制优先」——你自己放内存(C/C++),悬垂指针的风险也全归你4。
② Rust 的第三条路:给指针用法立规矩,而不是给运行时加机制
Rust 两个妥协都不接受。它的办法反直觉:不解决矛盾,而是限制你的程序,让矛盾发生不了—— 给指针的使用加上一套规则,规则严格到编译器能在编译期证明:你的程序没有内存安全错误5。
关键在运行时:程序跑起来,指针就是普通地址,和 C 一模一样;省下的全部成本在编译期付。
③ 所有权:每个值有唯一一个主人
规则的地基:每个值有恰好一个所有者(owner),主人负责决定它什么时候死。
变量(存值的有名格子)离开作用域就销毁,值跟着销毁。
结构体(把几种数据打包成一个类型)拥有它的字段(结构体内部的成员)。
向量(长度可变的数组)拥有它的元素——所有拥有关系拼成一棵树, 树根是变量,树死则整棵死6。
这样一来「什么时候释放」天然清楚,但树太死:现实世界要传值、要共享、要引用。
④ 移动:赋值不是复制,是过户
Rust 的第一步松动:赋值、传参、返回,默认都不是复制,是移动——值的所有权从源搬到目标, 源变量变成「未初始化」,不许再用。好处:和 C++ 深复制一样产权清晰, 和 Python 一样便宜(搬的只是向量头的三个机器字,堆上的数据原地不动)7。
⑤ 再松动三处:复制类型、计数指针、借用
光移动还不够,规则再开三个口子:
- Copy 类型(整数、浮点、布尔这类纯位模式的值)免除移动,按位复制、源照常用;
- Rc/Arc:需要多个主人时,用引用计数指针共享——克隆只加计数,最后一个指针死了值才死;
- 借用(引用):不接管所有权,只是临时指向——这是用得最多的口子,下一章整章讲它。
⑥ 借用:「借来的,必须还;借了只读的,谁都别想改」
引用是不拥有所有权的指针,规矩两条:共享引用只读,但可以同时有很多个; 可变引用能读能写,但同一时刻只能有一个,连主人都被冻结——「多读,或单写」8。
而且引用绝不允许活得比它指向的值长。怎么查?给每个引用指派一个「生命周期」(程序里一段区间的名字), 约束只有两句:被借的值的寿命要覆盖引用的寿命;存引用的变量要求引用覆盖它的寿命。 编译器找一组同时满足全部约束的寿命——找不到,就拒绝编译。函数签名(函数的类型声明:收什么输入、返回什么)、 结构体定义都要把这些写出来,签名即承诺:看签名就知道函数会把参数(传进函数的值)存到哪、 返回的引用指向谁9。
⑦ 回报:一整类经典 bug 不是被修了,是写不出来
「多读或单写」+「寿命约束」在编译期执行,效果不止内存安全:
-
C++ 的自赋值翻车(自己把自己正要复制的资源关了;资源,即内存、文件句柄这类被值占着的东西), 恰好都被「可变访问即排他」消灭10;
-
挨个取元素(行话叫迭代)的工具——迭代器——会失效;还有 memcpy 源和目标重叠, 全是「以为在改 A、其实在同时读写同一个东西」的同一类错;
-
数据竞争(两个线程同时读写同一处内存,结果取决于谁快)的定义就是「某个值既可变又被跨线程共享」—— 这正是引用规则禁止的形状, 所以一个不用 unsafe 的 Rust 并发(多个线程同时跑)程序按构造就是无数据竞争的11。
⑧ 代价与作者的态度
代价:所有权这套机制给「随便连」的对象图(书里叫 sea of objects)设了减速带, 逼你做前期设计。作者的总结很直白:Rust 是把理解程序的痛苦从未来挪到现在12。
这条链每章的展开:第 ①–② 步和第 3 章开头一起讲,第 ③–⑤ 步在第 3 章, 第 ⑥–⑦ 步在第 4 章,第 ① 步之前的「值长什么样」在第 2 章,语法(语言的拼写规则)细节在第 5 章。
3. 章节地图
这张表不用记任何术语——每章名字后面写的是「读完你能回答什么问题」。
| 章 | 读完你就能回答 |
|---|---|
| 01 语言巡礼 | Rust toolchain 长什么样?一个带测试和命令行参数的小程序怎么从零跑起来?错误没有异常时怎么传? |
| 02 曼德博程序与并发 | 一个真实的多线程画图程序怎么串起泛型、match、切片、?、scoped 线程?「安全看不见」是什么意思? |
| 03 数值、元组与指针家族 | u8/i32/f64 这套命名怎么读?溢出到底 panic 还是回绕?引用、Box、Rc、裸指针各管什么? |
| 04 数组、向量与字符串(一串字符的文本) | 数组/向量/切片什么区别?String 和 &str 为什么是两个类型?len() 为什么数的是 8 个二进制位而不是字符个数? |
| 05 所有权 | 为什么内存管理是二选一?所有权树怎么理解?移动语义(这套操作的含义)凭什么既便宜又清晰? |
| 06 移动实战与共享所有权 | 循环里/下标处怎么移动?Copy 谁说了算?Rc/Arc 的计数怎么工作、坑在哪? |
| 07 引用的用法 | 共享引用和可变引用各能干什么?& 和 * 什么时候要写、什么时候不用写?为什么不能有空引用? |
| 08 引用为什么安全 | 编译器靠什么拒绝悬垂指针?生命周期参数怎么省略?「多读或单写」消灭了哪些经典 bug? |
| 08b 引用规则实战判例 | 一组具体代码逐个判:哪些能编译、哪些被拒?C 的 const 差在哪?对象之海是什么? |
| 09 表达式与控制流(代码执行的走向) | 为什么 Rust 没有三元运算符?分号到底什么意思?match 为什么必须覆盖所有情况? |
| 10 调用、运算符与类型转换 | turbofish 是什么?as 转换哪些安全哪些禁止?&String 凭什么能当 &str 用? |
推荐顺序: 01 → 02 → 03 → 04 → 05 → 06 → 07 → 08 → 08b → 09 → 10,大体是原书顺序(08b 是第 4 章的实战配套,读完 08 顺手读)。 只关心「Rust 为什么安全」的,读 05、06、07、08 四章就够;急着想写代码的,01、02、09、10 是语法地图。
4. 这本书覆盖什么、不覆盖什么
覆盖(已放出章节,读完你能回答):
- 安装与工具链(rustup、cargo、rustc、rustdoc),以及 Cargo 怎么管理包和依赖;
- 写三个完整程序:命令行 gcd(含单元测试)、Actix web 服务、多线程曼德博画图;
- 所有基本类型的内存布局与边界行为:整数溢出四族处理、浮点、bool、元组、数组、向量、切片、字符串;
- 所有权、移动、Copy、Rc/Arc 的完整机制与内存图示;
- 引用的全部规则与生命周期检查的推理过程,含函数签名、结构体里的寿命参数;
- 表达式语言的全部语法:块、分号、声明、遮蔽、if/match/if let/let-else、四种循环、break/continue/return、
!类型; - 运算符细节(整除、取余、位运算优先级)、
as转换规则、deref 强转、闭包语法。
不覆盖(别指望在这份 Early Release 里找):
| 缺什么 | 说明 |
|---|---|
| 第 6 章及以后全部 | 错误处理深入、模块、结构体与 enum、模式匹配、trait 与泛型、集合、迭代器与闭包深入、I/O、并发原语(语言和库直接提供的基础构件)、unsafe、宏、FFI、async/await——整本后半还没放出,书内引用全部挂着 [Link to Come] |
| trait 系统 | 只有第 1 章巡礼级介绍(FromStr、derive),没有系统讲解 |
| unsafe 与裸指针 | 只有名字,没有展开 |
| async 机制 | 第 1 章演示了用法,底层模型(reactor、轮询)不在已放出章节 |
| 2024 edition 的迁移细节 | 书按 2024 edition 写,但没有逐条对照旧版讲差异 |
5. 我们的判断
判断(我们的,不是书里的): 这份 Early Release 的价值排序是—— ① 第 3、4 章所有权/借用的推理链(硬通货,不过时)> ② 第 2 章的类型与内存布局(可靠参考)> ③ 第 1、5 章的语法巡礼(会随 edition 小幅调整,但骨架稳定)。 引用它时引①②;书里所有「[Link to Come]」指向的内容都不存在,别当成这本书讲了。 如果错,会错在: 第三版正式版可能改动已放出章节的结构和措辞——届时锚点需要回核, 但所有权模型自 Rust 1.0 起没动过,①的论断不会过时。
判断(我们的,不是书里的): 这本书对「编程语言与系统研究」书库的最大用处, 是提供一个「用类型系统兑现内存安全」的完整教学样本:问题定义(①的矛盾)→ 方案(所有权树)→ 检查办法(寿命约束求解)→ 收益盘点(⑦的 bug 清单)。 这套「先立规矩、再做检查、最后数收益」的论证结构,可以当模板去分析其他系统语言的安全设计。 如果错,会错在: 如果研究问题转向「怎么给已有语言补安全」(如 C 的 Checked C、 内存安全沙箱(把代码关进受控环境里运行的隔离技术)), 这个「从零设计」的模板参考价值会打折——它教的是设计语言,不是修补语言。
Footnotes
-
出处:「Chapter 1. A Tour of Rust」第 35–40 段(
text/04-ch01-chapter-1-a-tour-of-rust.txt:35-40,搜「cargo 1.85.0」)。三个命令各自报出版本:cargo 1.85.0 (2024-12-31)、rustc 1.85.0 (2025-02-17)。 ↩ -
出处:「Chapter 1. A Tour of Rust」第 13 段(
text/04-ch01-chapter-1-a-tour-of-rust.txt:13,搜「what gives the language its character」)。原话:让 Rust 有性格的「不是某个具体的、惊人的特性可以放在第一页炫耀,而是所有部分协同工作的方式」,目标是「安全的、高性能的系统编程」。 ↩ -
出处:「Chapter 3. Ownership and Moves」第 37 段(
text/13-ch03-chapter-3-ownership-and-moves.txt:37,搜「radical wager」)。原文把「即使有这些限制,语言仍然足够灵活」称作「Rust 的激进赌注(Rust's radical wager)」,并明说「对于从 C/C++ 过来的我们,这笔交易不用想」。 ↩ -
出处:「Chapter 3. Ownership and Moves」第 15–27 段(
text/13-ch03-chapter-3-ownership-and-moves.txt:15-27,搜「Safety First」与「Control First」)。两派的原文标签是 "Safety First" camp 与 "Control First" camp。 ↩ -
出处:「Chapter 3. Ownership and Moves」第 31 段(
text/13-ch03-chapter-3-ownership-and-moves.txt:31,搜「bring just enough order」)。原文:「通过给指针的使用施加一些限制……给混沌带来刚好足够的秩序,让 Rust 的编译期检查能验证程序没有内存安全错误」;「在运行时,你的指针就是内存中的简单地址,和 C/C++ 一样」。 ↩ -
出处:「Chapter 3. Ownership and Moves」第 119–123 段(
text/13-ch03-chapter-3-ownership-and-moves.txt:119-123,搜「form trees」)。原文:「所有者和被拥有的值构成树:你的所有者是你的父节点,你拥有的值是你的子节点……每个 Rust 程序里的每个值都是某棵树的一员,树根是某个变量。」 ↩ -
出处:「Chapter 3. Ownership and Moves」第 198–210 段(
text/13-ch03-chapter-3-ownership-and-moves.txt:198-210,搜「three header fields」与「use of moved value」)。移动只搬走向量的三个机器字头部;再用已搬走的源会报「use of moved value」。 ↩ -
出处:「Chapter 4. References」第 111–115 段(
text/14-ch04-chapter-4-references.txt:111-115,搜「multiple readers or single writer」)。原文把共享/可变引用的区别说成「在编译期执行的多读或单写规则」。 ↩ -
出处:「Chapter 4. References」第 432–434 段(
text/14-ch04-chapter-4-references.txt:432-434,搜「signature always exposes」)。原文:「在 Rust 中,函数的签名总是暴露函数体的行为」——签名里没有 'static 的函数,绝不可能把参数存进全局。 ↩ -
出处:「Chapter 4. References」第 852–908 段(
text/14-ch04-chapter-4-references.txt:852-908,搜「self-assignment」与「overlap」)。书里用 C++ 的 File 自赋值例子说明:clone_from(&mut f, &f) 在 Rust 直接编译失败。 ↩ -
出处:「Chapter 4. References」第 910 段(
text/14-ch04-chapter-4-references.txt:910,搜「free of data races by construction」)。原文:「一个避免 unsafe 代码的 Rust 并发程序按构造就是无数据竞争的」。 ↩ -
出处:「Chapter 4. References」第 952 段(
text/14-ch04-chapter-4-references.txt:952,搜「transferring the pain」)。原话:「Rust 就是把理解你的程序的痛苦从未来转移到现在的全部。」 ↩