读书导读 · MIT / Apache-2.0

《Rust 程序设计语言》全书摘要与逐章导读

MIT / Apache-2.0

引文按 MIT 或 Apache-2.0 许可使用。免费阅读原书。

《Rust 程序设计语言》讲了什么AI 辅助

一套所有权模型,从数据设计一路扩展到多线程服务器

一门语言怎么能既安全又快?Rust 的回答:把保证搬进编译器。本书分层搭建。它从社区和工具(rustup、Cargo、crate)以及小程序开始,然后安装核心模型:默认不可变的绑定、静态类型、所有权、借用和切片。结构体、枚举、Option 和模式匹配,把这个模型变成数据设计;模块和私有性把它扩展到包;泛型、trait 和单态化,在不产生运行时代价的情况下消除重复;Result、? 和 panic! 把失败放进类型系统。一个 minigrep 项目把这些部件转化为工程实践——关注点分离、测试、闭包、迭代器。后面的章节把同样的所有权规则向外推:用于堆数据、共享数据和内部可变数据的智能指针;用于无畏并发的线程、通道以及 Mutex/Arc;用于 I/O 密集型工作的异步 future 和运行时;用于没有继承的多态的 trait 对象和模式;unsafe 作为一道狭窄、经审计的逃生口。最后一个多线程网络服务器,靠跟着编译器错误走、在 TCP 上手工搭建,把几乎所有东西重新组合起来。自始至终,论证是一个重复的主张:现在接受摩擦,这样拒绝整类 bug 的是编译器,而不是运行时的运气。

一则讲透全书的寓言AI 辅助

用一个故事装下全书最核心的想法,并老实交代故事在哪里失真。

白天写下来,否则夜里会把你烧掉

从前有个村子,每盏灯笼都恰好有一位保管人,村口的大门由一位老太太把守,在你的文书对齐之前,她不让任何人通过。一盏灯笼可以交给另一位保管人,但这样第一位保管人的手里就空了,她不能假装还拿着;相比之下,便宜的锡哨子谁要都可以复制一个。旅人可以借灯笼的光——许多人可以同时借着它读书,但一旦有人想剪灯芯,其他所有读书的人都得退后,而且谁也不许从一座已经烧毁的房子里把光带出来。一个年轻的信使等得不耐烦了。“放我过去,”他说,“我会小心的;我会记得哪盏灯笼是谁的。”守门人拒绝了:“我不信半夜里的记忆。在这儿,在白天,把你的文书排整齐,夜晚就烧不到你。”于是他在门口站了一个小时,咒骂着,每当她指着一行,他就重写一遍他的路线单,直到文书终于对上了——她一言不发地打开了大门,过关不收他分文。他跑了一整夜,一次也没有撞过,一次也没有去够一盏已经熄灭的灯笼。后来村子需要更多的人手,同一位守门人检查了夜班队、一条链上带着计数登记的共享灯笼、一次只有一个工人能持有钥匙的密封库房,以及承诺会回来、而不是闲站着的信使。她还留着一扇狭窄的侧门,有标记,有见证,给那些必须不带文书过关的人,但她把他们的名字写进一本册子,并请一位邻居去读它。

故事里的每样东西代表什么

故事里书里
每盏灯笼一位保管人,交出去之后手里是空的所有权:每个值恰好有一个所有者,赋值会移动拥有堆数据的值,并使原来的失效
可以随意复制的锡哨子Copy 类型,复制而不是移动
借来的光:许多读者,但剪灯芯的人排斥所有其他人;烧毁的房子里不能带出光引用与借用:共享借用可以共存,可变借用排斥所有其他借用,悬垂引用会被拒绝
在白天检查文书、过关不收费的守门人编译器作为守门人:编译期检查以零运行时开销提供内存安全——没有垃圾回收,没有手动 free
每当她指着一行,信使就重写他的路线单由编译器驱动、基于项目的方法:现在接受摩擦,让每个编译器错误指引下一块内容
他“我会小心的”这番恳求被拒绝核心主张:保证必须能被编译器验证,而不是托付给运行时的自律
夜班队、带计数登记的链式灯笼、单钥匙的库房无畏并发:同样的所有权和类型检查覆盖线程、Rc/Arc 计数,以及由 Mutex 保护的共享状态
承诺会回来、而不是闲站着的信使异步 future 在运行时上被 await,而不是每次调用都阻塞一个线程

寓言在哪里失真

这则寓言的扭曲之处,在于让守门人的检查看起来完全免费,又完全是她的功劳。书承认了故事容纳不下的代价:边界检查、RefCell 在运行时的借用检查、原子引用计数,以及每个具体类型一份编译出的特化,都是过了大门之后付出的真实代价。它也容纳不下书里根本不是所有权的那一半——Option 和 Result 把缺失和失败放进类型系统,配上穷尽的 match,trait 和 trait 对象提供没有继承的多态,模块和私有性塑造公共 API,以及测试去弥补类型检查弥补不了的缝隙,因为守门人验证文书,却从不验证意图。而侧门是最深的扭曲:安全的叙事需要一条被许可的打破它的途径,所以最终的保证落在人工审计上(以及只看得到真正运行过的代码的 Miri),而不是守门人身上。最后,整个村子是被论证的,而不是被度量的——书的证据是它自己的小示例清单,一个命令行工具和一个有意没有完成的网络服务器,没有任何基准测试。

《Rust 程序设计语言》的核心论点

全书捍卫的 10 个论点,按论证需要的顺序排列。

  1. 长期被接受的、在高层人体工学和低层控制之间的取舍是不必要的,因为安全的抽象和泛型,编译出的代码和手写的具体代码一样快。

    “在编程语言设计中,高层的人体工学和底层的控制往往互相矛盾;Rust 挑战这种冲突。”

    “High-level ergonomics and low-level control are often at odds in programming language design; Rust challenges that conflict.”

    Introduction查看原文 →
  2. Rust 系统性地拿程序员的便利,去换取编译器能验证的保证,而不是信任运行时的自律,就连它的集合 API 也是由内存布局而不是便利塑造的。
  3. 编译期的所有权检查以零运行时代价提供内存安全,所以既不需要垃圾回收,也不需要手动 free,而引用是对所有权必要的补充,而不是可有可无的便利。
  4. 缺失和失败属于类型系统,而不属于空值或异常:因为 Option<T> 和 Result<T,E> 与 T 是不同的,而且 match 必须是穷尽的,编译器迫使调用者在部署之前承认它们。
  5. 表明契约被打破的失败,值得立即终止,而可恢复的失败应当作为值返回,并传播给一个掌握函数所缺少的上下文的调用者,永远不要以原始 panic 的形式暴露给用户。
  6. 并发安全不是与内存安全分开的问题——同样的所有权和类型检查,把并发 bug 推到了编译期——然而一门低层的语言仍应提供好几种并发工具,而不是一个教条的机制。
  7. async 只是编译器生成的状态机之上的语法糖,而为运行时声明任务,胜过每个阻塞调用一个线程,因为每个线程的系统开销最终会占主导,而程序可以利用操作系统看不到的并发。
  8. Rust 在把数据与过程打包、并通过私有性和 trait 对象强制执行封装方面是面向对象的,但有意省略了继承,因为继承共享了比预期更多的东西,并缩窄了设计的灵活性。
  9. 默认私有的模块,加上深思熟虑的 pub use 重导出,是正确的默认值,因为隐藏内部使作者能在不破坏调用者的情况下改变内部代码,而公共 API 应当为用户而塑造,而不是为作者的文件布局。
  10. 因为类型检查验证的是类型而不是意图,正确性必须由带有信息丰富的失败报告的测试来断言,而程序最好的成长方式,是让编译器错误指引下一块内容,然后重构,使每个函数负责一项任务。

逐章导读

每一章都在本页:论证怎样展开、关键论点、常见误读和核心概念。从目录里选一章。

导言

第4章的所有权系统,究竟是如何不带运行时代价地兑现所承诺的安全的?

导言是怎样展开的AI 辅助

本章不是从定义开始的,而是从一个论点开始。在一段简短的出版说明之后,它欢迎读者,并立刻划出全书的核心前提:高层的人体工学和低层的控制“往往互相矛盾”,而 Rust“挑战这种冲突”。这是个修辞上高效的动作——它在解释这门语言的任何事情之前,先告诉读者 Rust 为解决什么问题而存在,所以此后的每个主张,都可以被读作对那一个命题的证据。

展开:一次受众调查,从宽到窄

正文随后通过问“Rust 是为谁而设”并以五幅相继的肖像来回答而展开。这个顺序是有意的,而不是随意的:它从开发者团队开始,那里论证的是,编译器作为“守门人”,抓住别的语言留给评审和测试的 bug——并且工具(Cargo、rustfmt、语言服务器)被提出来,作为系统编程不必感觉原始的证据。从那里它向外拓宽到学生(可及性,一个欢迎人的社区),公司(一长串生产用途,以 Firefox 这个显眼的例子结尾),以及开源开发者(一份贡献的邀请)。最后一幅肖像,重视速度和稳定的人,是绕回开篇的那一幅:通过零成本抽象,“安全的代码”也意在成为“快的代码”。这一节以最强的形式重述论点作结——Rust“最大的雄心,是消除程序员几十年来所接受的取舍”。

转折:从论证到指示

一个清晰的支点随之而来。“本书为谁而写”和“如何使用本书”把语域从说服转向定向:文字不再做关于 Rust 的主张,而开始做关于读者在书中路径的主张。先决条件被设定(你以前编过程;用什么语言无所谓),一个结构上的区分被引入(概念章节对项目章节,第2、12、21章被指名为项目),然后是逐章的巡礼,从安装和 Cargo,经过所有权、结构体和枚举、模块、集合、错误处理、泛型和生命周期、闭包和迭代器、智能指针、并发和异步、OOP 比较、模式,到高级主题——结束于多线程网络服务器。附录 A 到 G 最后列出。值得注意的是,这番巡礼还提供了一条分支路线:不耐烦的读者从第2章开始,一丝不苟的读者跳到第3章,再回来。

收尾:许可,然后是一条实用的警告

本章以放松它刚刚规定的顺序结束——“读这本书没有错误的方式”——然后加上唯一一条真正是教学而不是组织性的建议:学会读编译器的错误信息,本身就是学习 Rust 的一部分。因此,本书将有意包含无法编译的例子,并警告读者,运行一个随机的片段可能按设计失败。一个吉祥物(Ferris)被引入,作为此类代码的视觉标记,之后一行指向 GitHub 上源码的提示结束了全章。

关键论点,以及支撑它们的原文

论点 1

Rust 的构建,是为了消解令人愉快的高层语言与对低层细节的真正控制之间的通常取舍。

作者同时从两头论证这一点:编译器拒绝那些通常让低层工作痛苦的、难以捉摸的 bug,而特性被设计成编译出与手写等价物一样快的代码,所以控制几乎不花人体工学的代价,人体工学也几乎不花速度的代价。

“在编程语言设计中,高层的人体工学和底层的控制往往互相矛盾;Rust 挑战这种冲突。”

“High-level ergonomics and low-level control are often at odds in programming language design; Rust challenges that conflict.”

Introduction查看原文 →
论点 2

Rust 还把现代开发者工具带进了一个历来缺乏它们的领域,这是同一个人体工学论证的一部分。

点名了三件具体的工具:Cargo 在整个生态系统中一致地处理依赖和构建,rustfmt 强制执行一种共享的代码风格,Rust 语言服务器为编辑器提供补全和行内错误——这些都是系统程序员以前没有的便利。

“在编程语言设计中,高层的人体工学和底层的控制往往互相矛盾;Rust 挑战这种冲突。”

“High-level ergonomics and low-level control are often at odds in programming language design; Rust challenges that conflict.”

Introduction查看原文 →
论点 3

这不是纸上的愿望:这门语言已经在承担真实的生产工作。

作者指出,数百家大小公司用 Rust 做命令行工具、网络服务、DevOps、嵌入式设备、音视频转码、生物信息学、搜索、物联网、机器学习,以及 Firefox 浏览器的主要部分。注意这是一份采用者广度的清单,而不是与其他语言的度量比较。

“在编程语言设计中,高层的人体工学和底层的控制往往互相矛盾;Rust 挑战这种冲突。”

“High-level ergonomics and low-level control are often at odds in programming language design; Rust challenges that conflict.”

Introduction查看原文 →
另外 1 个论点
论点 4

Rust 所提供的“速度”,既涵盖程序跑得多快,也涵盖你能多快地持续修改它们。

作者把 Rust 的编译期检查,与没有它们的语言里那些脆弱的遗留代码——那里开发者不敢碰能工作的代码——相对照;因为这些检查在新特性和重构中都成立,这里的稳定意味着修改的自由,而零成本的目标也让由此产生的安全代码保持快速。

“在编程语言设计中,高层的人体工学和底层的控制往往互相矛盾;Rust 挑战这种冲突。”

“High-level ergonomics and low-level control are often at odds in programming language design; Rust challenges that conflict.”

Introduction查看原文 →

它是怎样运作的

本章论证所依靠的 3 个机制。

机制 1

编译器在评审之前就阻止 bug

低层代码藏着微妙的错误,别的语言只能靠大量测试和专家评审才暴露出来。Rust 的编译器拒绝构建含有这些错误的代码,包括并发方面的,所以一个技能参差的团队争论的是程序逻辑,而不是追捕缺陷。

机制 2

廉价的抽象让安全保持快速

Rust 的目标是看起来高层、却编译成与手写低层版本做同样机器工作的特性。因为抽象不增加运行时代价,选择安全的写法,并不意味着接受一个更慢的程序。

机制 3

坏掉的例子教人读错误

既然编译器阻止有缺陷的代码,进步就取决于理解它说的是什么。所以本章说,书会印出失败的例子连同确切的信息,把每一次拒绝变成通往可工作代码的路标,而不是死胡同。

常见误读

你可能以为

如果编译器拒绝构建有 bug 的代码,Rust 项目几乎不需要测试。

其实

编译器只阻止它能检查的那几类错误,比如内存和并发故障;你的程序是否算出正确的答案,超出它的能力之外,这就是为什么作者专门用一整章讲测试,恰恰因为 Rust 的安全保证不涵盖逻辑。

“在 Rust 中,编译器扮演守门人的角色,拒绝编译带有这些难以捉摸的 bug 的代码,包括并发 bug。”

“In Rust, the compiler plays a gatekeeper role by refusing to compile code with these elusive bugs, including concurrency bugs.”

Introduction查看原文 →
你可能以为

零成本抽象意味着任何 Rust 程序自动跑得和手工调优的低层代码一样快。

其实

作者把这呈现为语言所追求的方向——编译成快的低层代码的高层特性——所以它是特性被设计方式的一个性质,而不是你写的任何东西都出来是最优的保证。

“通过追求零成本抽象——编译成与手写代码一样快的更低层代码的更高层特性——Rust 力求让安全的代码同时也是快的代码。”

“By striving for zero-cost abstractions—higher-level features that compile to lower-level code as fast as code written manually—Rust endeavors to make safe code be fast code as well.”

Introduction查看原文 →

导言的核心概念

零成本抽象
编译成与手写低层代码一样快的更高层特性,所以安全的代码也可以是快的。
适用处: 是语言特性的设计目标,而不是每个程序自动最优的承诺。
编译器作为守门人
编译器拒绝构建含有难以捉摸的低层和并发 bug 的代码,所以团队争论的是逻辑,而不是追捕 bug。
适用处: 它阻止它能检查的那几类 bug;逻辑的正确性仍需要测试。
概念章节与项目章节
概念章节讲授 Rust 的一个方面;项目章节构建小程序,应用所学的东西。
适用处: 只有第2章、第12章和第21章是项目章节。
另外 0 个概念

导言划线与批注AI 陪读

Introduction

“高层的人体工学和底层的控制往往互相矛盾”

“High-level ergonomics and low-level control are often at odds”

查看原文 →

整本书的论点压缩在一个从句里。注意它被框定为设计空间的冲突,而不是技术上的冲突——Rust 的推销是,这个取舍一直是习惯,而不是定律。

Introduction

“编译器扮演守门人的角色,拒绝编译”

“the compiler plays a gatekeeper role by refusing to compile”

查看原文 →

被当作解放来兜售,但它也是权威的转移:评审的负担从资深的人转移到一台无法与之争辩的机器上。与借用检查器搏斗,是这里没人印出来的成本项。

Introduction

“Rust 语言服务器为集成开发提供支持”

“The Rust Language Server powers integrated development”

查看原文 →

一本把章节编号更新到 21 的书里,一块过时的化石。RLS 多年前就已被弃用,改用 rust-analyzer——工具那一段,比它所称赞的语言老得更快。

另外 5 条批注
Introduction

“加密货币、生物信息学、搜索引擎”

“cryptocurrencies, bioinformatics, search engines”

查看原文 →

公司采用的清单做着论证的工作:以广度证明成熟。但一份领域的清单不是一份结果的清单——从来没有人列出被放弃的 Rust 重写。

Introduction

“没有这些检查的语言里那些脆弱的遗留代码,开发者往往不敢去修改”

“the brittle legacy code in languages without these checks, which developers are often afraid to modify”

查看原文 →

本章情感上最精确的一句话。Rust 真正的卖点不是速度或安全——而是重新获得碰自己代码的许可。

Introduction

“如果你完全是编程新手,你最好”

“If you’re entirely new to programming, you would be better served”

查看原文 →

诚实,却悄悄地与两页之前的“学生”一节相矛盾,那一节承诺让系统概念“对更多的人变得可及,尤其是编程新手”。欢迎的地垫上有一级台阶。

Introduction

“我们会提供许多无法编译的例子,连同错误信息”

“we’ll provide many examples that don’t compile along with the error message”

查看原文 →

一种真正不寻常的教学承诺:把失败当作主要的文本来教,而不是当作附录。大多数语言书都把它们的坏代码藏起来。

Introduction

“在大多数情况下,我们会引导你找到任何无法编译的代码的正确版本。”

“In most situations, we’ll lead you to the correct version of any code that doesn’t compile.”

查看原文 →

这句话连续出现了两次——第二次孤零零地搁在 Ferris 表格应该在的地方。一条说明,在转换成纯文本时丢了它的图片。

读后感AI 陪读

章节结构坦白了行文从未坦白的东西:Rust 哪里痛

一篇同时干三件活的引言——宣言、受众分流和用户手册——前一件明显做得比后一件好。宣言很紧凑:人体工学与控制被陈述为一个假的两难,零成本抽象是逃生口,还有一个真正动人的观察:不安全语言最深的代价,是对你自己代码库的恐惧。受众分流是它摇晃的地方:编程新手在一节里受到欢迎,又在另一节里被客气地改道,而作为成熟度的、公司领域的广度清单,证明了采用,却没有证明满意。自始至终,编译器被拟人化为盟友和守门人,而本章从未点出这笔交易的价格——与一个总是赢的工具争论的漫长学徒期。阅读地图是最有用的部分,也是最有揭示性的:第2章可以跳过,所有权在结构体之前就有自己的一章,异步需要在线程之后加一章,这是对语言实际上哪里学起来痛的一幅素描。两个文字化石——Rust 语言服务器,和一处在 Ferris 的表格丢失的地方被重复的说明——提醒我们,这是一份活的、打了补丁的文档,对一本其主题推崇无畏重构的书来说,这本身相当符合品牌调性。

在 FeedLitra 里读导言,每段旁边都有这些卡片。

开始读书

论证在哪里吃力

不是反驳,而是细心的读者会想追问的地方。

  1. 安全被宣传为在运行时零成本,然而边界检查、RefCell 的运行时借用检查和原子引用计数,恰恰就是运行时成本。

    “Rust 通过立即退出,而不是允许这次内存访问并继续运行,来保护你免受这种错误。”

    “Rust protects you against this kind of error by immediately exiting instead of allowing the memory access and continuing.”

    §3.2 Data Types查看原文 →
  2. 穷尽匹配被认为使空值不可能,但同一本书又提供了 _、if let 和 let...else,其便利让程序员能跳过提供这个保证的覆盖。

    “尤其是对 Option<T> 而言,当 Rust 阻止我们忘记明确处理 None 的情形时,它保护我们不去假定自己有一个值,而实际上我们可能拿到的是空值,从而使前面讨论的那个十亿美元的错误成为不可能。”

    “Especially in the case of Option<T>, when Rust prevents us from forgetting to explicitly handle the None case, it protects us from assuming that we have a value when we might have null, thus making the billion-dollar mistake discussed earlier impossible.”

    §6.2 The match Control Flow Construct查看原文 →
  3. panic 被谴责为面向用户的输出,同时又充当测试机制,和对越界索引的、获得认可的响应。

    “不可恢复的错误总是 bug 的症状,比如试图访问数组末尾之外的位置,所以我们想要立即停止程序。”

    “Unrecoverable errors are always symptoms of bugs, such as trying to access a location beyond the end of an array, and so we want to immediately stop the program.”

    Chapter 9. Error Handling查看原文 →
  4. 提供许多并发工具,削弱了单一无畏路径的承诺:仍然是程序员,而不是编译器,必须在通道、Mutex/Arc、线程和异步之间做选择,而书在论证了异步在开销上胜出之后,仍把线程留给 CPU 密集型工作。

    “因此,Rust 提供了多种工具,以适合你的情形和需求的任何方式对问题建模。”

    “Therefore, Rust offers a variety of tools for modeling problems in whatever way is appropriate for your situation and requirements.”

    Chapter 16. Fearless Concurrency查看原文 →
另外 4 处张力,以及 8 条证据上的局限
  1. 安全的叙事需要一条官方认可的打破它的途径,所以保证最终落在对 unsafe 块的人工审计上,而不是编译器上。

    “让 unsafe 块保持小;以后你调查内存 bug 时会感谢自己的。”

    “Keep unsafe blocks small; you’ll be thankful later when you investigate memory bugs.”

    §20.1 Unsafe Rust查看原文 →
  2. 所有权的单一所有者规则,被呈现为安全机制,然而引用、克隆和引用计数紧接着就被引入,因为这条规则直接使用的成本太高。

    “但对于一个本应常见的概念来说,这太繁文缛节,也太费事了。”

    “But this is too much ceremony and a lot of work for a concept that should be common.”

    §4.1 What is Ownership?查看原文 →
  3. 默认私有保护了作者改变内部的自由,却迫使持续不断的 pub、use 和 pub use 工作,所以模块系统隐藏条目,只是为了有意把同样这些条目重新暴露出来。

    “Rust 选择让模块系统以这种方式运作,使隐藏内部实现细节成为默认。”

    “Rust chose to have the module system function this way so that hiding inner implementation details is the default.”

    §7.3 Paths for Referring to an Item in the Module Tree查看原文 →
  4. 零成本的说法忽略了每个具体类型一份特化的编译时间和代码体积代价,正如线程池里单一的共享互斥锁,把线程池本该并行化的分发步骤串行化了。

    “好消息是,使用泛型类型不会使你的程序运行得比使用具体类型更慢。”

    “The good news is that using generic types won’t make your program run any slower than it would with concrete types.”

    §10.1 Generic Data Types查看原文 →

证据上的局限

  1. 证据基础几乎完全是书自己的小示例清单和编译器错误码,而不是在真实代码库上度量出的结果。

    “在编程语言设计中,高层的人体工学和底层的控制往往互相矛盾;Rust 挑战这种冲突。”

    “High-level ergonomics and low-level control are often at odds in programming language design; Rust challenges that conflict.”

    Introduction查看原文 →
  2. 性能与效率的说法,是靠对代码如何编译或系统开销的推理来论证的,没有报告任何基准测试。

    “好消息是,使用泛型类型不会使你的程序运行得比使用具体类型更慢。”

    “The good news is that using generic types won’t make your program run any slower than it would with concrete types.”

    §10.1 Generic Data Types查看原文 →
  3. 所有权的论证,建立在对重复释放的推理之上,而不是经验性的缺陷数据。

    “所有权的任何特性都不会在程序运行时拖慢你的程序。”

    “None of the features of ownership will slow down your program while it’s running.”

    §4.1 What is Ownership?查看原文 →
  4. 工程教训来自一个小的命令行程序和一个有意没有完成的手工搭建的网络服务器,所以它们在更大系统上的普适性是被断言的,而不是被演示的。

    “最好把功能分开,让每个函数负责一项任务。”

    “It’s best to separate functionality so that each function is responsible for one task.”

    §12.3 Refactoring to Improve Modularity and Error Handling查看原文 →
  5. 并发演示依赖于操作系统的调度,和标准库不附带的外部运行时 crate,所以关于顺序和公平性的说法,只对所用的配置成立。

    “注意,当 Rust 程序的主线程完成时,所有已生成的线程都会被关闭,无论它们是否已经运行完毕。”

    “Note that when the main thread of a Rust program completes, all spawned threads are shut down, whether or not they have finished running.”

    §16.1 Using Threads to Run Code Simultaneously查看原文 →
  6. 所展示的内部可变性模式是单线程的,而引用计数的叙述省略了循环和泄漏,限定了智能指针这个故事能走多远。

    “在 Rust 中,由于所有权和借用的概念,引用和智能指针之间还有一个额外的区别:引用只借用数据,而在许多情况下,智能指针拥有它们所指向的数据。”

    “In Rust, with its concept of ownership and borrowing, there is an additional difference between references and smart pointers: While references only borrow data, in many cases smart pointers own the data they point to.”

    Chapter 15. Smart Pointers查看原文 →
  7. Miri 只分析真正执行的代码,所以它能表明未定义行为的存在,却永远无法表明它的不存在。

    “换句话说:如果 Miri 确实抓到了一个问题,你就知道有 bug,但仅仅因为 Miri 没抓到 bug,并不意味着没有问题。”

    “Put another way: If Miri does catch a problem, you know there’s a bug, but just because Miri doesn’t catch a bug doesn’t mean there isn’t a problem.”

    §20.1 Unsafe Rust查看原文 →
  8. 有几处验证只是单一案例:一个从未触及字形簇的多字节字符串例子,以及只对照一行字面请求检查的路由,没有涵盖其他方法、头部或格式错误的输入。

    “因此,答案是,为了避免返回一个意外的值,造成可能不会立即被发现的 bug,Rust 压根不编译这段代码,并在开发过程的早期就防止误解。”

    “The answer, then, is that to avoid returning an unexpected value and causing bugs that might not be discovered immediately, Rust doesn’t compile this code at all and prevents misunderstandings early in the development process.”

    §8.2 Storing UTF-8 Encoded Text with Strings查看原文 →

全书概念导图

5 组概念:通俗的叫法、一句话解释,以及原书自己的用语。

1 · 没有速度代价的人体工学
泛型和抽象编译出与手写一样的速度
更高层的构造编译成与手写低层代码一样高效的代码:类型占位符让一个定义服务于许多具体类型,而编译器为实际用到的每个类型发出一份特化的副本。原书用语: Zero-cost abstraction via monomorphization
编译器在代码运行之前就拒绝不匹配的代码
编译器拒绝类型或内存使用对不上的代码,而不是去猜测或强制转换,而这种模式延伸到设计:给每个状态或工作流阶段它自己的类型,使无效的操作编译不过。原书用语: Compiler as gatekeeper
测试证明类型检查无法验证的行为
正确性是类型检查弥合不了的缺口,转而由带 assert_eq!/assert_ne!、should_panic、返回 Result 的测试和 Debug 输出的 #[test] 函数来断言;测试框架的标志可以调整线程、输出和过滤,代码旁边的单元测试可以接触私有条目,而集成测试只使用公共接口,常常是测试先行。原书用语: Automated tests
通过构建程序、跟着错误信息走来学习
本书把概念章节与构建程序的章节交替,并教授先写出你希望存在的调用代码,让每个编译器错误指引下一块内容,然后重构,使每个函数负责一项任务,并把逻辑移出 main。原书用语: Compiler-driven, project-based method
2 · 一个所有者,没有垃圾回收器
每个值有一个所有者,结束时被清理
每个值恰好有一个所有者,当该所有者的作用域结束时被丢弃;赋值会移动拥有堆数据的值并使原来的失效,而 Copy 类型则是复制。同一条规则支配着通道发送、线程捕获,以及消耗 self 的状态转换。原书用语: Ownership
借用一个值来读它或改它,而不拿走它
引用是指向别处拥有的数据的、保证有效的地址;借用是使用一个值再把它还回去,可变借用排斥所有其他引用,悬垂引用被拒绝,切片借用一段连续的元素,而未完成的借用会阻止可能移动集合内存的修改。原书用语: References, borrowing, and slices
拥有堆数据并清理它的类指针类型
表现得像指针、却拥有其数据的结构体,通过 Deref 和 Drop 获得能力:栈存放固定大小的值,而堆存放大小未知的数据,Box 增加了让递归类型有已知大小的间接引用,解引用强制转换自动转换引用,Rc 允许多个所有者,而 RefCell 允许在运行时检查的内部可变性。原书用语: Smart pointers
Vec、String 和 HashMap,由内存的布局方式塑造
由堆支撑的容器,其 API 遵循它们的内存布局:Vec 连续地、可增长地存放一种类型,String 是一个可增长的 UTF-8 字节向量,可以按字节、标量值或字形簇来读,而 HashMap 通过哈希,用 entry API 把键映射到值。原书用语: Standard collections
3 · 缺失和失败变成了类型
枚举列出一个值可能的形式,包括什么都没有
一种自定义类型,列出值可能采取的各种形式,每个变体带着它自己的类型和数据量,这也让一个集合能容纳混合的形状;Option 的 Some/None 编码了存在或不存在,而不是空值,并且是与 T 不同的类型。原书用语: Enums and Option<T>
match 按形状选择分支,且必须涵盖每种情形
模式描述数据的形状并给其各部分命名;match 运行第一个吻合的分支,且必须涵盖每一个可能的值,而 _、if let、let...else、while let 和 for 则只处理一个模式,一个模式是否可能匹配失败,决定了它合法地可以出现在哪里。原书用语: Pattern matching and refutability
把失败作为值返回,只在违背承诺时才 panic
可恢复的失败是一个值——Ok 或 Err,? 提前返回并通过 From 转换——而被打破的契约或不变量会 panic,打印一条消息、展开并退出;unwrap 和 expect 走的就是这条路,而越界访问两种形式都提供:会 panic 的索引,或返回 Option 的 get。原书用语: Error handling: Result, ?, and panic!
类型在编译期固定,值除非选择加入否则冻结
每个值的类型在编译期已知,尽可能推断,但函数参数上是必需的;let 绑定除非标记为 mut 否则是固定的,常量则始终是固定的,而遮蔽在同一个名字下引入一个新变量(可能是另一种类型)。原书用语: Static types and immutable-by-default bindings
4 · 共享行为,隐藏内部
trait 为许多类型都能实现的共享行为命名
trait 命名一组类型实现的方法签名,以共享行为(只能在 trait 或类型是本地的地方实现);约束要求泛型类型实现它们,而通用实现涵盖每个满足另一个约束的类型;指针后面的 dyn Trait 持有任何实现者,分发推迟到运行时,提供没有子类化的多态。原书用语: Traits, trait bounds, and trait objects
默认私有;只发布你想保持稳定的 API
crate 是编译器的工作单位,包把它们和一个 Cargo.toml 捆在一起;模块嵌套在 crate 根之下,通过绝对或相对路径(包括 super)到达。条目除非标记为 pub,否则对父模块是私有的,use 缩短路径,而 pub use 重导出一条为用户而塑造的公共路径,而不是照搬内部结构。原书用语: Modules, privacy, and public API design
Cargo 构建你的项目,并引入社区的 crate
rustup 安装工具链;Cargo 搭建项目骨架,预先构建成独立的二进制文件,按 SemVer 说明符获取 crate,锁定解析出的版本,并管理发布配置、文档注释与文档测试、共享同一个 target 目录的工作空间、自定义子命令,以及 crates.io 上的撤回。原书用语: Cargo and the crate ecosystem
在你要求下一个值之前,迭代器什么也不做
一个 trait,唯一必需的方法 next,产出 Some 直到 None,并且在被消耗之前不做任何工作:像 sum 这样的消费适配器在内部驱动它,像 map 和 filter 这样的适配器返回新的迭代器——future 采取的是同样的惰性形状。原书用语: Iterators and laziness
5 · 编译器检查的并发
线程通过通道传递数据,或在锁之下共享它
无畏并发意味着编译器能抓住并发的错误:每个生成的线程是一个返回可 join 句柄的操作系统线程,通道通过转移所有权传递消息,Mutex 加 Arc 在锁之下共享状态,Send 和 Sync 是类型层面的许可,而由 worker 组成的线程池,在固定的上限下,从 Arc<Mutex<receiver>> 队列里拉取任务。原书用语: Threads, channels, and shared state
异步把等待的工作变成运行时调度的任务
future 是一个现在还没就绪、但最终会解析出来的值;async 标记一个可中断的块,await 是它可能让出的点。Rust 不带运行时,所以由一个外部的 crate 来调度和驱动 future——适合交错的、I/O 密集型的并发,不同于并行的、CPU 密集型的工作。原书用语: Async, futures, and runtimes
闭包把它们捕获的数据带进线程和任务
匿名的、可存储的函数,其参数和返回类型由首次使用推断;闭包捕获其函数体所需的最小访问——不可变借用、可变借用,或所有权(由 move 强制)——而这个选择决定了它是 Fn、FnMut 还是 FnOnce,进而决定了它能不能发送到线程,或者能不能被装箱为只发送一次的任务。原书用语: Closures and capture modes

值得停下来的段落AI 陪读

几处值得停下来的原文,每处附一句它为什么对全书要紧。

第 4 章 · §4.1 What is Ownership?

“所有权的任何特性都不会在程序运行时拖慢你的程序。”

“None of the features of ownership will slow down your program while it’s running.”

查看原文 →

全书核心的期票——安全在编译期开账单——而之后的每一章,要么在兑现它,要么在悄悄重新谈判它。

导言 · Introduction

“高层的人体工学和底层的控制往往互相矛盾”

“High-level ergonomics and low-level control are often at odds”

查看原文 →

全书的论点句:Rust 的赌注是,人体工学与控制之间的取舍是一种习惯,而不是定律。

导言 · Introduction

“编译器扮演守门人的角色,拒绝编译”

“the compiler plays a gatekeeper role by refusing to compile”

查看原文 →

点出书从未定价的那个隐藏的转移:评审的权威,从资深的人转移到一台你无法与之争辩的机器,这是第9到第15章的情感脊梁。

另外 9 条批注
第 20 章 · §20.1 Unsafe Rust

“Rust 内部藏着第二门语言,它不强制执行这些内存安全保证”

“Rust has a second language hidden inside it that doesn’t enforce these memory safety guarantees”

查看原文 →

十九章的保证,建立在一扇始终未锁的门上,直到这里,书才打开它。

第 20 章 · §20.1 Unsafe Rust

“宁可拒绝一些有效的程序,也比接受一些无效的程序要好”

“it’s better for it to reject some valid programs than to accept some invalid programs”

查看原文 →

解释了书中每一场与借用检查器的搏斗的那一行:编译器并不聪明,而是有意悲观。

第 6 章 · §6.1 Defining an Enum

“你排除的特性同样重要”

“the features you exclude are important too”

查看原文 →

对 Rust 的身份是由拒绝构成的最清楚的陈述——空值、异常、继承、数据竞争——这是半个章节列表背后的组织逻辑。

第 3 章 · §3.1 Variables and Mutability

“Rust 编译器保证,当你声明一个值不会改变时,它真的不会改变,所以你不必自己去追踪它。”

“The Rust compiler guarantees that when you state that a value won’t change, it really won’t change, so you don’t have to keep track of it yourself.”

查看原文 →

整门语言的生成原则:每一项限制,都是从你的脑子里卸载给机器的簿记,而所有权、生命周期和 Send/Sync,都是更大规模上同样的交易。

第 11 章 · Chapter 11. Writing Automated Tests

“Rust 的类型系统扛起了这个负担的很大一部分,但类型系统不能抓住一切。”

“Rust’s type system shoulders a huge part of this burden, but the type system cannot catch everything.”

查看原文 →

书最重要的让步——编译期的保证止于意图——也是它让测试、错误处理和 unsafe 这几章不至于多余的原因。

第 15 章 · Chapter 15. Smart Pointers

“引用只借用数据,而在许多情况下,智能指针拥有它们所指向的数据。”

“While references only borrow data, in many cases smart pointers own the data they point to.”

查看原文 →

事后解决了书中最深的、反复出现的问题:从第9章起,从 String 到 Rc 的一切,一直是沿着所有权这条轴组织的,而不是指针元数据。

第 16 章 · Chapter 16. Fearless Concurrency

“团队发现,所有权和类型系统是一套强大的工具,可以帮助管理内存安全和并发问题!”

“the team discovered that the ownership and type systems are a powerful set of tools to help manage memory safety and concurrency problems!”

查看原文 →

书安静地承认,它最好的特性是个副作用——内存纪律原来就是并发纪律,这是任何路线图都没预料到的。

第 9 章 · Chapter 9. Error Handling

“Rust 没有异常”

“Rust doesn’t have exceptions”

查看原文 →

错误从一个看不见的控制流通道,搬进了类型签名,这与 Option 是同一个动作,也是 Rust 的啰嗦是结构性的、而不是风格性的原因。

第 21 章 · §21.1 Building a Single-Threaded Web Server

“我们其实并不是在遍历连接。相反,我们是在遍历连接尝试”

“we’re not actually iterating over connections. Instead, we’re iterating over connection attempts”

查看原文 →

最终项目最尖锐的一行,追溯地为整个第14章辩护:Result 之所以出现,是因为尝试不等于成功,而整个由类型编码的易错性设计,都从这个区分而来。

读后感AI 陪读

程序员视作自然的那些取舍,其实是历史的偶然

从头读到尾,《Rust 程序设计语言》不是一次特性巡礼,而是一个持续的论证:程序员当作自然而接受的那些取舍——安全对速度,控制对人体工学——是历史的偶然,其中大多数可以通过把知识搬进类型系统、并在程序运行之前就解决掉而消解。从前言里管理维护的机构承诺,到最后的线程池,一切都是对这一个主张的展开。脊梁分四个乐章。第一,适应:安装和猜数游戏让你上手的,与其说是语法,不如说是与编译器的工作关系,而《通用编程概念》开始了全书真正的节奏——一份便利被收回,然后作为保证被偿还(默认不可变、必需的标注、边界检查、for 优于带索引的 while)。第二,核心:所有权、借用和切片,用检查器强制执行的规则,取代了 GC 和手动 free;结构体和枚举让数据形状变得明确,直到 Option<T> 加穷尽的 match 使空值错误无法表示;模块、集合、错误,以及泛型/trait/生命周期,然后把同样的纪律延伸到可见性、堆的成本、失败和有效性。第三,工程现实:测试之所以存在,恰恰因为类型钉住形状,却从不钉住意图,而 I/O 项目、Cargo 和工作空间表明,编译器的保证止于设计判断和生态惯例开始的边界。第四,诚实的撤退:智能指针、并发、异步、OOP 特性、模式和 unsafe 各是一个标了价的出口——RefCell 用运行时 panic 换掉编译期检查,匹配守卫用表现力换掉穷尽性,trait 对象用静态分发换异构性,unsafe 用验证换恰好五种超能力——而书的诚信,在于点明每个出口的代价,而不是假装规则从不弯曲。

全书最尖锐的判断,也是最容易错过的,是 Rust 著名的安全根本不主要是关于内存的;而是关于把人的警惕搬进由机器检查的结构。并发就是证明:没有为线程发明新的子系统,只是把所有权规则瞄准了一个新的目标,而 send 移动它的值,与第9章的移动是同一个想法。推论是,书的这些范畴是决定,而不是发现——“不可恢复”的错误之所以不可恢复,是因为 Rust 宣布它们如此,而博客文章状态机优于经典状态模式,是关于你想在哪里付出的论证,不是一个中立的事实。第二尖锐的判断是零成本的说法,迭代器那一章是在汇编里真正挣得它,而不是断言它;那一个演示,就是整本书偏好更高层构造的许可证。我要对读者说的是:你绝不能略读的章节是《所有权》,你绝不能解读为无能的搏斗是与借用检查器的搏斗——书开篇的宽慰,说编译器错误并不意味着你是个糟糕的程序员,是承重的,因为挫折就是课程。把项目章节当作散文来读,而不是当作要抄的代码;I/O 项目从三十行的 main 走到一个经过测试的库,以及网络服务器对 crate 和异步的拒绝,教的是在编译器压力之下重构的方法,那是任何参考手册都传达不了的。而一旦你走到逃生口,就不要再问一个特性做什么,而要开始问它保证什么——新类型买到的是类型安全,而别名买到的只是简短,这个区分,在智能指针、trait 对象和 unsafe 里反复出现,才是书想留给你的真正技能。二十三章的规则,是为了让你在最后五章的取舍上变得称职。

关于作者:史蒂夫·克拉布尼克

Rust 核心团队的老成员,也是 Rust 之书背后的文档负责人

  • 有学位,但说他在学位之外学到的多得多5
  • Oxide Computer Company 的软件工程师,写 Rust5
  • Rust 文档团队的负责人,也是 Rust 的核心开发者之一2
  • 被认为参与了 Rust 最初的设计和实现3
  • 《Rust 程序设计语言》(本书)1
  1. Rust 程序设计语言
  2. 关于作者——rust-lang/book,nostarch/bio.md
  3. 史蒂夫·克拉布尼克——Rust 治理 / 人物
  4. 史蒂夫·克拉布尼克访谈:“只要我们交付有用的东西,Rust 就不怕不完美”

网络书评怎么说

书评人一致称这是标准的、权威的 Rust 指南,称赞它讲解清楚、示例好,并涵盖了生态系统——cargo、构建系统、包管理器和测试。抱怨也反复出现:大约 500 页,对有经验的程序员来说太长太慢,借用检查器、生命周期、trait 和并发这几章让人觉得不堪重负,而社区的惯例几乎没得到关注。

综合已发表书评的看法;不展示评分,完整书评请点来源链接。

  1. 书评:Rust 程序设计语言 - DEV Community
  2. 书评——《Rust 程序设计语言》第二版
  3. Rust 程序设计语言
  4. 史蒂夫·克拉布尼克和卡罗尔·尼科尔斯的《Rust 程序设计语言》 - DEV Community

来源与授权

MIT / Apache-2.0原书

  • 《Rust 程序设计语言》(The Rust Programming Language),作者 Steve Klabnik、Carol Nichols、Chris Krycho,及 Rust 社区贡献者。Copyright © The Rust Project Developers。可任选 MIT 许可证(https://opensource.org/licenses/MIT)或 Apache 许可证 2.0 版(https://www.apache.org/licenses/LICENSE-2.0)使用。免费阅读原书 · 许可证
  • 摘要、论点、批注和读后感由 FeedLitra 根据全书原文 AI 辅助生成,引文已对照原文核对。最近更新:2026 年 10 月 21 日。

在 FeedLitra 里一边读《Rust 程序设计语言》,一边看这些卡片。

开始读书