- 教程
- 文档
【免费下载链接】book
The Rust Programming Language
《The Rust Programming Language》(TRPL)官方仓库在 packages/trpl 目录下维护着一个名为trpl的支撑 crate,专门服务于书中第 17 章异步编程(Async/Await)的教学示例。本文以该 crate 的 CHANGELOG.md 为主线,结合 源码实现、集成测试 与书籍正文,系统梳理 0.1.0 → 0.3.0 三个版本的 API 演进、命名对齐策略与向后兼容设计,帮助读者理解"教学用 crate"如何在功能迭代与读者体验之间取得平衡。
一、trplcrate 的定位:为什么书中需要一个专用支撑 crate
在深入变更记录之前,有必要先理解trplcrate 在整个项目中的角色。按 README.md 和 lib.rs 顶部注释所述,这个 crate本身几乎不实现业务逻辑,绝大部分只是对其他 crate 的再导出(re-export),它存在的原因有两点:
- 单一依赖、单一导入集合:读者在跟随书籍做练习时,只需在
Cargo.toml中添加trpl一个依赖,就能获得异步章节所需的全部类型、trait 和函数,不必逐个引入futures、tokio、tokio-stream、reqwest、scraper等 crate。 - 隔离上游变更风险:由于
trpl的内容与更新节奏完全由 book 仓库控制,即使上游出现破坏性变更(例如 Tokio 发布 breaking 的 2.0),读者手中的示例也不会被波及。
该 crate 在技术选型上以tokio作为底层异步运行时,因为书中认为它"经过充分测试且被广泛使用";futurescrate 则是Futuretrait 的最初诞生地,是 Rust 官方异步实验的家园。在部分场景下,trpl会对原始 API 进行重命名或包装,这正是 CHANGELOG.md 的核心内容所在。
提示:根据 Cargo.toml,当前 crate 版本为
0.3.0,使用 Rust2024edition,最低 Rust 版本要求为 1.79(见 README),许可证为MIT OR Apache-2.0。
二、0.1.0:为异步章节首版草稿而生的初始发布
0.1.0在变更日志中只有一句话:"Initial release! Adds support code for the first draft of the new async chapter of the book."
这是整个 crate 的起点:它为书籍新版异步章节(第 17 章)的首版草稿提供了支撑代码。从当前 Cargo.toml 可以反推,即使是在初始阶段,其依赖体系就已覆盖了教学所需的核心能力:
futures = "0.3":提供join、join_all、future::select、Either等组合子;tokio = "1":提供异步运行时与fs、rt-multi-thread、sync、time等特性;tokio-stream = "0.1":提供Stream、StreamExt及各类流适配器;reqwest、scraper:分别用于 HTTP 请求与 HTML 解析(在 0.2.0 中正式进入公开 API)。
初始发布确立的设计原则——"大而全地再导出、统一命名、隔离上游"——在后续版本中一直被严格延续。
三、0.2.0:为第 17 章更多示例补充get、Response与Html
0.2.0的变更记录为:
Added
get,Response, andHtmlto support more examples in chapter 17.
这三个新增项全部服务于书中"第一个异步程序"的教学场景——通过 URL 抓取网页并解析 HTML 标题。它们对应 lib.rs 中的三处实现:
3.1get:简化版的 HTTP GET
/// Fetch data from a URL. For more convenient use in _The Rust Programming /// Language_, panics instead of returning a [`Result`] if the request fails. pub async fn get(url: &str) -> Response { Response(reqwest::get(url).await.unwrap()) }get直接包装reqwest::get,但做了一处面向教学的关键决策:请求失败时直接panic!,而不是返回Result。这与trpl一贯的"示例代码尽可能简洁、聚焦异步概念本身"的设计取向一致——书中示例无需处理错误分支,读者可以专心观察async/.await的行为。
3.2Response:轻量响应包装
pub struct Response(reqwest::Response); impl Response { pub async fn text(self) -> String { self.0.text().await.unwrap() } }Response是对reqwest::Response的薄包装(thin wrapper),唯一公开方法text()同样以unwrap取代Result。在书中示例里,trpl::get(url).await.text().await可以像同步代码一样链式书写。
3.3Html:基于scraper的选择器查询
pub struct Html { inner: scraper::Html, } impl Html { pub fn parse(source: &str) -> Html { ... } pub fn select_first<'a>(&'a self, selector: &'a str) -> Option<scraper::ElementRef<'a>> { ... } }Html包装scraper::Html,提供parse(解析 HTML 文档)与select_first(按 CSS 选择器取第一个匹配元素)两个方法。select_first在 selector 非法时也会 panic,同样是"为教学便利而简化"的体现。
3.4 测试佐证
tests/integration/main.rs 中的re_exported_html测试验证了该 API 的教学语义:
let doc = Html::parse("<html><head><title></title></head><body><p>Hello!</p></body></html>"); let p = doc.select_first("p").map(|el| el.inner_html()); assert_eq!(p, Some(String::from("Hello!")));get与Response的实际用法可参见书籍正文 ch17-01-futures-and-syntax.md,其中page_title函数正是通过trpl::get(url).await.text().await拉取页面文本。
四、0.3.0:面向生态术语对齐的命名调整与依赖升级
0.3.0是变更日志着墨最多的版本,它宣称"This is intended to be a backwards-compatible release"(意图保持向后兼容),包含三类变化:
4.1 方法重命名:与主流异步 crate 术语对齐
0.3.0 最重要的变更是重命名,原因是接受了技术评审(tech review)反馈——trpl早期使用的命名与主流异步 crate 的习惯术语不一致:
| 旧名称 | 新名称 | 说明 |
|---|---|---|
race | select | 在futures等生态中,"竞速选择"的惯用名是select |
run | block_on | 与 Tokio 生态Runtime::block_on、futures::executor::block_on等命名对齐 |
这与 README.md 中"让读者使用与生态一致的一组导入"的定位完全吻合——教学命名不应与社区惯例产生认知摩擦。
4.2 依赖与工具链升级
Upgraded Rust, the edition,
ring, andquinn-protoSwitched torustls
结合 Cargo.toml 可以看到本次升级的落地证据:edition 升级到2024,且reqwest依赖被配置为default-features = false并显式启用rustls-tls特性——这正是"Switched torustls"的直接体现,即 TLS 后端从native-tls(其底层依赖ring/quinn-proto等)切换为纯 Rust 实现的rustls。
4.3 源码中的兼容性细节
重命名并非简单替换,lib.rs 中保留了旧名称作为兼容别名,并附有详细文档注释说明缘由:
/// This function has been renamed to `block_on`; please see its documentation. /// This function remains to maintain compatibility with the online versions /// of the book that use the name `run`. pub fn run<F: Future>(future: F) -> F::Output { block_on(future) }race同样保留,并内部委托给select:
pub async fn race<A, B, F1, F2>(f1: F1, f2: F2) -> Either<A, B> where F1: Future<Output = A>, F2: Future<Output = B>, { select(f1, f2).await }保留旧名称的理由在 tests/integration/main.rs 中写得很清楚:线上版本的异步章节曾以旧名称发布,若直接删除会导致那些章节的示例无法编译。这种"新增名称 + 保留别名"的双轨策略,就是backwards-compatible承诺的具体实现。
五、从变更日志到公开 API 全貌:0.3.0 的完整能力清单
将 CHANGELOG 与 lib.rs 结合,可以还原 0.3.0 的完整公开 API 面:
5.1 函数与宏
| 名称 | 来源/实现 | 教学用途 |
|---|---|---|
block_on(future) | 自实现,内部新建 TokioRuntime并block_on | 在同步main中驱动异步代码(书中第 17 章的标准写法) |
run(future) | block_on的兼容别名 | 兼容旧版在线章节 |
join(a, b) | 再导出futures::future::join | 同时等待两个 future |
join3(a, b, c) | 再导出futures::future::join3 | 同时等待三个 future |
join_all(futures) | 再导出futures::future::join_all | 等待一组 future,返回JoinAll |
join! | 再导出futures::join宏 | 以宏形式同时等待多个 future |
select(f1, f2) | 自实现,基于futures::future::select+pin! | 竞速:先完成的胜出并丢弃另一个 |
race(f1, f2) | select的兼容别名 | 兼容旧版在线章节 |
spawn_task(future) | 再导出tokio::task::spawn | 将 future 作为独立任务调度 |
yield_now() | 再导出tokio::task::yield_now | 主动让出当前执行权 |
sleep(duration) | 再导出tokio::time::sleep | 异步休眠 |
interval(duration) | 再导出tokio::time::interval | 周期定时器 |
channel() | 再导出tokio::sync::mpsc::unbounded_channel | 异步消息通道 |
stream_from_iter(iter) | 再导出tokio_stream::iter | 由迭代器构造Stream |
read_to_string(path) | 再导出tokio::fs::read_to_string | 异步读取文件 |
get(url) | 自实现,包装reqwest::get | 异步 HTTP GET |
5.2 类型与 Trait
| 名称 | 来源/实现 | 说明 |
|---|---|---|
Sender/Receiver | 再导出tokio::sync::mpsc::{UnboundedSender, UnboundedReceiver} | 无界异步通道两端 |
JoinHandle | 再导出tokio::task::JoinHandle | 任务句柄 |
Either | 再导出futures::future::Either | select的结果类型 |
Stream/StreamExt | 再导出tokio_stream::{Stream, StreamExt} | 流抽象 |
IntervalStream | 再导出tokio_stream::wrappers::IntervalStream | 定时器流 |
ReceiverStream | 再导出tokio_stream::wrappers::UnboundedReceiverStream | 通道接收端流 |
Response/Html | 自实现(包装reqwest/scraper) | 网页抓取与解析 |
5.3 一个值得注意的通道设计决策
lib.rs 中有一段注释专门解释了通道 API 的取舍:tokio::sync::mpsc::channel(有界)对应std::sync::mpsc::sync_channel,而tokio::sync::mpsc::unbounded_channel才对应std::sync::mpsc::channel。为了不让学生在学习异步时被"为什么突然出现 unbounded"这类问题分心,trpl::channel()直接选用unbounded 变体并映射到熟悉的Sender/Receiver命名——这是"教学优先"设计哲学的又一个实例。
六、测试如何守护兼容性承诺
tests/integration/main.rs 是一个单一的集成测试 crate(其头部注释说明这是刻意遵循的最佳实践:每个集成测试都是独立二进制,统一收拢在一个 crate 中便于管理)。测试矩阵与 CHANGELOG 的兼容性承诺一一对应:
using_run_works与race_continues_to_work:验证旧名称run、race仍可用;re_exported_block_on_works:注释明确指出它是所有其他测试的地基,"如果它坏了,下面所有测试都会失败";re_exported_spawn_works、re_exported_sleep_works、re_exported_channel_apis_work:覆盖任务、休眠、通道等再导出;re_exported_join_apis_work模块:覆盖join、join3、join_all、join!四种并联形式;select测试:构造慢(1 秒)快(1 毫秒)两个 future,断言Either::Right(Fast)胜出;yield_now、read_to_string、stream_iter、receiver_stream、re_exported_interval_stream_works、re_exported_html:覆盖余下全部公开 API。
从测试组织可以看出,"旧名称持续可用"不是口头承诺,而是被自动化测试锁定的硬性约束;同时block_on被当作全 crate 的地基 API 重点守护。
七、在书籍中的实际使用位置
trpl的 API 贯穿整个第 17 章(异步章节),主要使用点包括:
- ch17-01-futures-and-syntax.md:首次介绍
trplcrate(cargo add trpl即可引入),使用trpl::get、Html、trpl::block_on、trpl::select、Either; - ch17-02-concurrency-with-async.md:
trpl::block_on驱动主流程、trpl::sleep、trpl::spawn_task、trpl::join、trpl::channel、trpl::join!; - ch17-03-more-futures.md:更多 future 组合应用;
- ch17-04-streams.md:
trpl::StreamExt、trpl::interval等流式 API; - ch17-05-traits-for-async.md:
trpl::join!到trpl::join_all的演进与JoinAll类型; - ch17-06-futures-tasks-threads.md:
trpl::spawn_task与任务模型、trpl::block_on的总结性应用。
例如书中经典的并发示例(对应 ch17-02-concurrency-with-async.md)会这样组织代码:
trpl::block_on(async { let (tx, mut rx) = trpl::channel(); // ... 在 tx 端发送消息、在 rx 端异步接收 ... });读者只需记住trpl一个 crate,即可完成从 runtime 驱动、任务调度、通道通信到流式处理、网页抓取的全部练习。
八、给读者的使用建议
- 版本匹配:书籍当前示例面向
trpl 0.3.0。如果你阅读的是早期发布的在线章节,代码中出现trpl::run或trpl::race属于正常现象——0.3.0 保留了这两个别名,依然可以编译运行;新代码建议优先使用block_on与select。 - 添加依赖:在练习项目中执行
cargo add trpl,即可获得与书籍完全一致的 API 面;源码可在 packages/trpl/src/lib.rs 查看每个导出项的真实来源与设计注释。 - 运行环境:
trpl 0.3.0要求 Rust 1.79+(见 packages/trpl/README.md)并使用 2024 edition(见 packages/trpl/Cargo.toml);其 TLS 能力基于rustls,不依赖系统原生 TLS 库。 - 注意教学简化:
get、Response::text、Html::select_first在失败时以panic!代替Result返回,这是刻意为之的教学简化;在生产代码中请使用底层的reqwest、scraper原始 API。
结语
从 0.1.0 的初始发布,到 0.2.0 补齐网页抓取能力,再到 0.3.0 完成术语对齐、工具链升级与 rustls 切换,trpl的三个版本记录了一条清晰的演进路径:始终围绕书籍教学需求收缩 API 面,同时通过兼容别名与集成测试,保证任何时刻出版的章节示例都能稳定运行。理解这份 CHANGELOG,不仅能帮你用好trpl,也能让你看到"教学用支撑库"在 API 设计上的一种值得借鉴的取舍方式。
- 教程
- 文档
【免费下载链接】book
The Rust Programming Language
相关推荐
jcode 社区与支持:AI 编程助手的 Discord 交流、Issue 反馈与求助完整指南
jcode 社区与支持:AI 编程助手的 Discord 交流、Issue 反馈与求助完整指南 jcode 是一款主打"最省内存(RAM efficient)"
教程文档Rust 异步编程权威指南:async/await、Future 与 Stream 深度解析(The Rust Programming Language 第 17 章)
Rust 异步编程权威指南:async/await、Future 与 Stream 深度解析(The Rust Programming Language 第 1
教程文档mdbook-trpl-note 预处理器解析:为《The Rust Programming Language》mdBook 构建语义化 Note 标签
mdbook trpl note 预处理器解析:为《The Rust Programming Language》mdBook 构建语义化 Note 标签 导读
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考