diff --git a/README.md b/README.md
index f9c33a21..53b3b840 100644
--- a/README.md
+++ b/README.md
@@ -10,26 +10,34 @@
- 在线阅读
- - 官方: [https://course.rs](https://course.rs)
- - 知乎: [支持章节内目录跳转,很好用!](https://www.zhihu.com/column/c_1452781034895446017)
+ - 官网: [https://course.rs](https://course.rs)
+ - 知乎专栏: [支持章节内目录跳转,很好用!](https://www.zhihu.com/column/c_1452781034895446017)
-- 本书配套项目
- - [Rust 实战练习](https://github.com/sunface/rust-by-practice),它是本书的配套练习册,提供了大量有挑战性的示例、练习和实践项目,帮助大家解决 Rust 语言从学习到实战的问题 — 毕竟这之间还隔着好几个 Go 语言的难度 :D
- - [Rust 语言周刊](https://github.com/sunface/rust-weekly),每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态
- - [Rust 酷库推荐](https://github.com/sunface/fancy-rust) Rust 优秀项目很多,如何在茫茫码海中与它们相遇?相比 Awesome Rust,它能带给你全新的体验和选择
+- 配套项目
+ - [Rust语言实战](https://github.com/sunface/rust-by-practice),它是本书的配套练习册,提供了大量有挑战性的示例、练习和实践项目,帮助大家解决 Rust 语言从学习到实战的问题 — 毕竟这之间还隔着好几个 Go 语言的难度 :D
+ - [Rust语言周刊](https://github.com/sunface/rust-weekly),每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态
+ - [Rust酷库推荐](https://github.com/sunface/fancy-rust),优秀项目很多,如何在茫茫码海中与它们相遇?相比 Awesome Rust,它能带给你全新的体验和选择
## 教程简介
**`Rust语言圣经`**涵盖从**入门到精通**所需的 Rust 知识,目录及内容都经过深思熟虑的设计,同时语言生动幽默,行文流畅自如,摆脱技术书籍常有的机器味和晦涩感。
-在 Rust 基础教学的同时,我们还提供了(部分):
+在 Rust 基础教学的同时,我们还提供了:
- **深入度**,在基础教学的同时,提供了深入剖析。浅尝辄止并不能让我们站上紫禁之巅
+
- **性能优化**,选择 Rust,就意味着要追求性能,因此你需要体系化地了解性能优化
-- **专题**,将 Rust 高级内容通过专题的形式一一呈现,内容内聚性极强
-- **难点和错误索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
-- **场景化模版**,程序员上网查询如何操作文件是常事,没有人能记住所有代码,场景化模版可解君忧
+
+- **专题内容**,将 Rust 高级内容通过专题的形式一一呈现,内容内聚性极强,例如手把手实现链表、Cargo和Tokio使用指南、async异步编程、标准库解析、WASM等等
+
+- **内容索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
+
+- **规避陷阱和对抗编译器**,只有真的上手写过一长段时间 Rust 项目,才知道该如何规避常见的陷阱以及解决一些难搞的编译器错误,而本书将帮助你大大缩短这个过程,提前规避这些问题
+
+- **Cook Book**,涵盖多个应用场景的实战代码片段,程序员上网查询文件操作、正则解析、数据库操作是常事,没有人能记住所有代码,而 Cook Book 可解君忧,Ctrl + C/V 走天下
+
+- **配套练习题**,像学习一门大学课程一样学习 Rust 是一种什么感觉?*Rust语言圣经 + Rust语言实战* 双剑合璧,给你最极致的学习体验
总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
@@ -41,6 +49,11 @@
> 在开源版权上,我们选择了 [No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB),这意味着读者可以随意的 fork 和阅读,但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,望理解。
+## 学习社区
+为了帮助大家更好的学习和交流 Rust,我们建立了一个社区:**Rust语言学习社区**。
+
+QQ群 1009730433, 欢迎大家加入,一起 happy,一起进步。
+
## 贡献者
@@ -51,6 +64,7 @@
- [@1132719438](https://github.com/1132719438)
- [@Mintnoii](https://github.com/Mintnoii)
- [@Rustln](https://github.com/rustln)
+- [@zongzi531](https://github.com/zongzi531)
尤其感谢这些主要贡献者,谢谢你们花费大量时间贡献了多处`fix`和高质量的内容优化。非常感动,再次感谢~~
@@ -64,6 +78,3 @@
- 详细清单参见 [这里](./assets/writing-material/books.md)
因为它们绝大部分是支持 APACHE + MIT 双协议的,因此我们选择了遵循其中的 MIT 协议,并在这里统一对借鉴的书籍进行说明。
-
-## Rust语言社区
-QQ群 1009730433, 欢迎大家加入,一起 happy,一起进步。
diff --git a/assets/Rust中英翻译对照表.md b/assets/Rust中英翻译对照表.md
index 111273f2..6c825129 100644
--- a/assets/Rust中英翻译对照表.md
+++ b/assets/Rust中英翻译对照表.md
@@ -301,7 +301,7 @@ RAII | 资源获取即初始化(一般不译) |
range | 区间,范围 |
range expression | 区间表达式 |
raw identifier | 原生标识符 |
-raw pointer | 原生指针,裸指针 |
+raw pointer | 裸指针 |
RC | 引用计数 | reference counted
reader | 读取器 |
reader/writer | 读写器 |
diff --git a/assets/sitemap.xml b/assets/sitemap.xml
index 6bb6863c..bb88977c 100644
--- a/assets/sitemap.xml
+++ b/assets/sitemap.xml
@@ -426,62 +426,62 @@
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
-
+
2021-12-30
weekly
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index cfc7e5fe..b3aea018 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -1,9 +1,9 @@
# Rust 语言圣经
[进入 Rust 编程世界](into-rust.md)
+[关于本书](about-book.md)
[AWS 为何这么喜欢 Rust?](usecases/aws-rust.md)
[避免从入门到放弃](sth-you-should-not-do.md)
-[关于本书](about-book.md)
[快速查询入口](index-list.md)
@@ -96,64 +96,104 @@
- [Macro 宏编程](advance/macro.md)
-
+
+ - [易混淆概念解析](advance/confonding/intro.md)
+ - [切片和切片引用](advance/confonding/slice.md)
+ - [Eq 和 PartialEq](advance/confonding/eq.md)
+ - [String、&str 和 str todo](advance/confonding/string.md)
+ - [裸指针、引用和智能指针 todo](advance/confonding/pointer.md)
+ - [作用域、生命周期和 NLL todo](advance/confonding/lifetime.md)
+ - [move、Copy 和 Clone todo](advance/confonding/move-copy.md)
+
## 专题内容,每个专题都配套一个小型项目进行实践
-
- [自动化测试](test/intro.md)
- [编写测试及控制执行](test/write-tests.md)
- [单元测试和集成测试](test/unit-integration-test.md)
- [断言 assertion](test/assertion.md)
- [用 Github Actions 进行持续集成](test/ci.md)
- [基准测试 benchmark](test/benchmark.md)
+
+- [Rust 异步编程](async-rust/intro.md)
+ - [async/await 异步编程](async-rust/async/intro.md)
+ - [async 编程入门](async-rust/async/getting-started.md)
+ - [底层探秘: Future 执行与任务调度](async-rust/async/future-excuting.md)
+ - [定海神针 Pin 和 Unpin](async-rust/async/pin-unpin.md)
+ - [async/await 和 Stream 流处理](async-rust/async/async-await.md)
+ - [同时运行多个 Future](async-rust/async/multi-futures-simultaneous.md)
+ - [一些疑难问题的解决办法](async-rust/async/pain-points-and-workarounds.md)
+ - [实践应用:Async Web 服务器](async-rust/async/web-server.md)
+ - [Tokio 使用指南](async-rust/tokio/intro.md)
+ - [tokio 概览](async-rust/tokio/overview.md)
+ - [使用初印象](async-rust/tokio/getting-startted.md)
+ - [创建异步任务](async-rust/tokio/spawning.md)
+ - [共享状态](async-rust/tokio/shared-state.md)
+ - [消息传递](async-rust/tokio/channels.md)
+ - [I/O](async-rust/tokio/io.md)
+ - [解析数据帧](async-rust/tokio/frame.md)
+ - [深入 async](async-rust/tokio/async.md)
+ - [select](async-rust/tokio/select.md)
+ - [类似迭代器的 Stream](async-rust/tokio/stream.md))
+ - [优雅的关闭](async-rust/tokio/graceful-shutdown.md)
+ - [异步跟同步共存](async-rust/tokio/bridging-with-sync.md)
-- [async/await 异步编程](async/intro.md)
- - [async 编程入门](async/getting-started.md)
- - [底层探秘: Future 执行与任务调度](async/future-excuting.md)
- - [定海神针 Pin 和 Unpin](async/pin-unpin.md)
- - [async/await 和 Stream 流处理](async/async-await.md)
- - [同时运行多个 Future](async/multi-futures-simultaneous.md)
- - [一些疑难问题的解决办法](async/pain-points-and-workarounds.md)
- - [实践应用:Async Web 服务器](async/web-server.md)
-- [Tokio 使用指南](tokio/intro.md)
- - [tokio 概览](tokio/overview.md)
- - [使用初印象](tokio/getting-startted.md)
- - [创建异步任务](tokio/spawning.md)
- - [共享状态](tokio/shared-state.md)
- - [消息传递](tokio/channels.md)
- - [I/O](tokio/io.md)
- - [解析数据帧](tokio/frame.md)
- - [深入 async](tokio/async.md)
- - [select](tokio/select.md)
- - [类似迭代器的 Stream](tokio/stream.md))
- - [优雅的关闭](tokio/graceful-shutdown.md)
- - [异步跟同步共存](tokio/bridging-with-sync.md)
-
-- [Cargo 使用指南](cargo/intro.md)
- - [上手使用](cargo/getting-started.md)
- - [基础指南](cargo/guide/intro.md)
- - [为何会有 Cargo](cargo/guide/why-exist.md)
- - [下载并构建 Package](cargo/guide/download-package.md)
- - [添加依赖](cargo/guide/dependencies.md)
- - [Package 目录结构](cargo/guide/package-layout.md)
- - [Cargo.toml vs Cargo.lock](cargo/guide/cargo-toml-lock.md)
- - [测试和 CI](cargo/guide/tests-ci.md)
- - [Cargo 缓存](cargo/guide/cargo-cache.md)
- - [Build 缓存](cargo/guide/build-cache.md)
- - [进阶指南](cargo/reference/intro.md)
- - [指定依赖项](cargo/reference/specify-deps.md)
- - [依赖覆盖](cargo/reference/deps-overriding.md)
- - [Cargo.toml 清单详解](cargo/reference/manifest.md)
- - [Cargo Target](cargo/reference/cargo-target.md)
- - [工作空间 Workspace](cargo/reference/workspaces.md)
- - [条件编译 Features](cargo/reference/features/intro.md)
- - [Features 示例](cargo/reference/features/examples.md)
- - [发布配置 Profile](cargo/reference/profiles.md)
- - [通过 config.toml 对 Cargo 进行配置](cargo/reference/configuration.md)
- - [发布到 crates.io](cargo/reference/publishing-on-crates.io.md)
- - [构建脚本 build.rs](cargo/reference/build-script/intro.md)
- - [构建脚本示例](cargo/reference/build-script/examples.md)
-
-- [手把手带你实现链表 doing](too-many-lists/intro.md)
+- [Rust 工具链指南](toolchains/intro.md)
+ - [Cargo 使用指南](toolchains/cargo/intro.md)
+ - [上手使用](toolchains/cargo/getting-started.md)
+ - [基础指南](toolchains/cargo/guide/intro.md)
+ - [为何会有 Cargo](toolchains/cargo/guide/why-exist.md)
+ - [下载并构建 Package](toolchains/cargo/guide/download-package.md)
+ - [添加依赖](toolchains/cargo/guide/dependencies.md)
+ - [Package 目录结构](toolchains/cargo/guide/package-layout.md)
+ - [Cargo.toml vs Cargo.lock](toolchains/cargo/guide/cargo-toml-lock.md)
+ - [测试和 CI](toolchains/cargo/guide/tests-ci.md)
+ - [Cargo 缓存](toolchains/cargo/guide/cargo-cache.md)
+ - [Build 缓存](toolchains/cargo/guide/build-cache.md)
+ - [进阶指南](toolchains/cargo/reference/intro.md)
+ - [指定依赖项](toolchains/cargo/reference/specify-deps.md)
+ - [依赖覆盖](toolchains/cargo/reference/deps-overriding.md)
+ - [Cargo.toml 清单详解](toolchains/cargo/reference/manifest.md)
+ - [Cargo Target](toolchains/cargo/reference/cargo-target.md)
+ - [工作空间 Workspace](toolchains/cargo/reference/workspaces.md)
+ - [条件编译 Features](toolchains/cargo/reference/features/intro.md)
+ - [Features 示例](toolchains/cargo/reference/features/examples.md)
+ - [发布配置 Profile](toolchains/cargo/reference/profiles.md)
+ - [通过 config.toml 对 Cargo 进行配置](toolchains/cargo/reference/configuration.md)
+ - [发布到 crates.io](toolchains/cargo/reference/publishing-on-crates.io.md)
+ - [构建脚本 build.rs](toolchains/cargo/reference/build-script/intro.md)
+ - [构建脚本示例](toolchains/cargo/reference/build-script/examples.md)
+
+- [Rust 最佳实践](practice/intro.md)
+ - [对抗编译检查](practice/fight-with-compiler/intro.md)
+ - [生命周期](practice/fight-with-compiler/lifetime/intro.md)
+ - [生命周期过大-01](practice/fight-with-compiler/lifetime/too-long1.md)
+ - [生命周期过大-02](practice/fight-with-compiler/lifetime/too-long2.md)
+ - [循环中的生命周期](practice/fight-with-compiler/lifetime/loop.md)
+ - [闭包碰到特征对象-01](practice/fight-with-compiler/lifetime/closure-with-static.md)
+ - [重复借用](practice/fight-with-compiler/borrowing/intro.md)
+ - [同时在函数内外使用引用](practice/fight-with-compiler/borrowing/ref-exist-in-out-fn.md)
+ - [智能指针引起的重复借用错误](practice/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md)
+ - [类型未限制(todo)](practice/fight-with-compiler/unconstrained.md)
+ - [幽灵数据(todo)](practice/fight-with-compiler/phantom-data.md)
+ - [Rust 常见陷阱](practice/pitfalls/index.md)
+ - [for 循环中使用外部数组](practice/pitfalls/use-vec-in-for.md)
+ - [线程类型导致的栈溢出](practice/pitfalls/stack-overflow.md)
+ - [算术溢出导致的 panic](practice/pitfalls/arithmetic-overflow.md)
+ - [闭包中奇怪的生命周期](practice/pitfalls/closure-with-lifetime.md)
+ - [可变变量不可变?](practice/pitfalls/the-disabled-mutability.md)
+ - [可变借用失败引发的深入思考](practice/pitfalls/multiple-mutable-references.md)
+ - [不太勤快的迭代器](practice/pitfalls/lazy-iterators.md)
+ - [奇怪的序列 x..y](practice/pitfalls/weird-ranges.md)
+ - [无处不在的迭代器](practice/pitfalls/iterator-everywhere.md)
+ - [线程间传递消息导致主线程无法结束](practice/pitfalls/main-with-channel-blocked.md)
+ - [警惕 UTF-8 引发的性能隐患](practice/pitfalls/utf8-performance.md)
+ - [日常开发三方库精选](practice/third-party-libs.md)
+ - [命名规范](practice/naming.md)
+ - [面试经验 doing](practice/interview.md)
+ - [代码开发实践 todo](practice/best-pratice.md)
+ - [日志记录 todo](practice/logs.md)
+ - [可观测性监控 todo](practice/observability.md)
+
+- [手把手带你实现链表](too-many-lists/intro.md)
- [我们到底需不需要链表](too-many-lists/do-we-need-it.md)
- [不太优秀的单向链表:栈](too-many-lists/bad-stack/intro.md)
- [数据布局](too-many-lists/bad-stack/layout.md)
@@ -169,48 +209,15 @@
- [Drop、Arc 及完整代码](too-many-lists/persistent-stack/drop-arc.md)
- [不咋样的双端队列](too-many-lists/deque/intro.md)
- [数据布局和基本操作](too-many-lists/deque/layout.md)
-- [易混淆概念解析](confonding/intro.md)
- - [切片和切片引用](confonding/slice.md)
- - [Eq 和 PartialEq](confonding/eq.md)
- - [String、&str 和 str todo](confonding/string.md)
- - [原生指针、引用和智能指针 todo](confonding/pointer.md)
- - [作用域、生命周期和 NLL todo](confonding/lifetime.md)
- - [move、Copy 和 Clone todo](confonding/move-copy.md)
-
-- [对抗编译检查 doing](fight-with-compiler/intro.md)
- - [幽灵数据(todo)](fight-with-compiler/phantom-data.md)
- - [生命周期](fight-with-compiler/lifetime/intro.md)
- - [生命周期过大-01](fight-with-compiler/lifetime/too-long1.md)
- - [生命周期过大-02](fight-with-compiler/lifetime/too-long2.md)
- - [循环中的生命周期](fight-with-compiler/lifetime/loop.md)
- - [闭包碰到特征对象-01](fight-with-compiler/lifetime/closure-with-static.md)
- - [重复借用](fight-with-compiler/borrowing/intro.md)
- - [同时在函数内外使用引用](fight-with-compiler/borrowing/ref-exist-in-out-fn.md)
- - [智能指针引起的重复借用错误](fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md)
- - [类型未限制(todo)](fight-with-compiler/unconstrained.md)
-
-- [Rust 常见陷阱](pitfalls/index.md)
- - [for 循环中使用外部数组](pitfalls/use-vec-in-for.md)
- - [线程类型导致的栈溢出](pitfalls/stack-overflow.md)
- - [算术溢出导致的 panic](pitfalls/arithmetic-overflow.md)
- - [闭包中奇怪的生命周期](pitfalls/closure-with-lifetime.md)
- - [可变变量不可变?](pitfalls/the-disabled-mutability.md)
- - [可变借用失败引发的深入思考](pitfalls/multiple-mutable-references.md)
- - [不太勤快的迭代器](pitfalls/lazy-iterators.md)
- - [奇怪的序列 x..y](pitfalls/weird-ranges.md)
- - [无处不在的迭代器](pitfalls/iterator-everywhere.md)
- - [线程间传递消息导致主线程无法结束](pitfalls/main-with-channel-blocked.md)
- - [警惕 UTF-8 引发的性能隐患](pitfalls/utf8-performance.md)
-
-- [Rust 最佳实践 doing](practice/intro.md)
- - [日常开发三方库精选](practice/third-party-libs.md)
- - [命名规范](practice/naming.md)
- - [代码开发实践 todo](practice/best-pratice.md)
- - [日志记录 todo](practice/logs.md)
- - [可观测性监控 todo](practice/observability.md)
- - [面试经验 doing](practice/interview.md)
-
-- [Rust 性能剖析 todo](profiling/intro.md)
+ - [Peek](too-many-lists/deque/peek.md)
+ - [基本操作的对称镜像](too-many-lists/deque/symmetric.md)
+ - [迭代器](too-many-lists/deque/iterator.md)
+ - [最终代码](too-many-lists/deque/final-code.md)
+ - [不错的unsafe队列](too-many-lists/unsafe-queue/intro.md)
+ - [数据布局](too-many-lists/unsafe-queue/layout.md)
+ - [基本操作](too-many-lists/unsafe-queue/basics.md)
+ - [Miri](too-many-lists/unsafe-queue/miri.md)
+- [Rust 性能优化 todo](profiling/intro.md)
- [深入内存 todo](profiling/memory/intro.md)
- [指针和引用 todo](profiling/memory/pointer-ref.md)
- [未初始化内存 todo](profiling/memory/uninit.md)
@@ -242,7 +249,7 @@
- [HashMap todo](std/hashmap.md)
- [Iterator 常用方法 todo](std/iterator.md)
-- [CookBook](cases/intro.md)
+- [CookBook todo](cases/intro.md)
- [命令行解析 todo](cases/cmd.md)
- [配置文件解析 todo](cases/config.md)
- [编解码 todo](cases/encoding/intro.md)
diff --git a/src/about-book.md b/src/about-book.md
index 08341cab..6c577d8b 100644
--- a/src/about-book.md
+++ b/src/about-book.md
@@ -12,55 +12,68 @@
- 在线阅读
- 官方: [https://course.rs](https://course.rs)
- 知乎: [支持章节内目录跳转,很好用!](https://www.zhihu.com/column/c_1452781034895446017)
-
-- 本书配套项目
- - [Rust 实战练习](https://github.com/sunface/rust-by-practice),它是本书的配套练习册,提供了大量有挑战性的示例、练习和实践项目,帮助大家解决 Rust 语言从学习到实战的问题 — 毕竟这之间还隔着好几个 Go 语言的难度 :D
- - [Rust 语言周刊](https://github.com/sunface/rust-weekly),每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态
- - [Rust 酷库推荐](https://github.com/sunface/fancy-rust) Rust 优秀项目很多,如何在茫茫码海中与它们相遇?相比 Awesome Rust,它能带给你全新的体验和选择
-### 教程简介
+- 配套项目
+ - [Rust语言实战](https://github.com/sunface/rust-by-practice),它是本书的配套练习册,提供了大量有挑战性的示例、练习和实践项目,帮助大家解决 Rust 语言从学习到实战的问题 — 毕竟这之间还隔着好几个 Go 语言的难度 :D
+ - [Rust语言周刊](https://github.com/sunface/rust-weekly),每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态
+ - [Rust酷库推荐](https://github.com/sunface/fancy-rust),优秀项目很多,如何在茫茫码海中与它们相遇?相比 Awesome Rust,它能带给你全新的体验和选择
+
+
+## 教程简介
**`Rust语言圣经`**涵盖从**入门到精通**所需的 Rust 知识,目录及内容都经过深思熟虑的设计,同时语言生动幽默,行文流畅自如,摆脱技术书籍常有的机器味和晦涩感。
-在 Rust 基础教学的同时,我们还提供了(部分):
+在 Rust 基础教学的同时,我们还提供了:
- **深入度**,在基础教学的同时,提供了深入剖析。浅尝辄止并不能让我们站上紫禁之巅
-- **性能优化**,选择 Rust,意味着就要追求性能,因此你需要体系化的了解性能优化
-- **专题**,将 Rust 高级内容通过专题的方式一一呈现,内容内聚性极强
-- **难点和错误索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
-- **场景化模版**,程序员上网查询如何操作文件是常事,没有人能记住所有代码,场景化模版可解君忧
-总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
+- **性能优化**,选择 Rust,就意味着要追求性能,因此你需要体系化地了解性能优化
-### 开源说明
+- **专题内容**,将 Rust 高级内容通过专题的形式一一呈现,内容内聚性极强,例如手把手实现链表、Cargo和Tokio使用指南、async异步编程、标准库解析、WASM等等
-在开源版权上,我们选择了 [No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB),这意味着读者可以随意的 fork 和阅读,但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,望理解。
+- **内容索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
-Rust 语言圣经是**完全开源**的电子书,每个章节都至少用时 4-6 个小时才能初步完稿,牺牲了大量休闲娱乐、陪伴家人的时间,还没有任何钱赚。
+- **规避陷阱和对抗编译器**,只有真的上手写过一长段时间 Rust 项目,才知道该如何规避常见的陷阱以及解决一些难搞的编译器错误,而本书将帮助你大大缩短这个过程,提前规避这些问题
-**如果大家觉得这本书作者真的用心了,希望你能帮我们点一个 🌟 `star`。感激不尽!:)**
+- **Cook Book**,涵盖多个应用场景的实战代码片段,程序员上网查询文件操作、正则解析、数据库操作是常事,没有人能记住所有代码,而 Cook Book 可解君忧,Ctrl + C/V 走天下
-### 借鉴的书籍
+- **配套练习题**,像学习一门大学课程一样学习 Rust 是一种什么感觉?*Rust语言圣经 + Rust语言实战* 双剑合璧,给你最极致的学习体验
-站在巨人的肩膀上,能帮我们看的更远,特此感谢以下巨人:
+总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
-- [Rust Book](https://doc.rust-lang.org/book)
-- [Rust nomicon](https://doc.rust-lang.org/nomicon/dot-operator.html)
-- [Async Rust](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html)
-- 详细清单参见 [这里](https://github.com/sunface/rust-course/blob/main/assets/writing-material/books.md)
+## ❤️ 开源
+本书是完全开源的,但是并不意味着质量上的妥协,这里的每一个章节都花费了大量的心血和时间才能完成,为此牺牲了陪伴家人、日常娱乐的时间,虽然我们并不后悔,但是如果能得到读者您的鼓励,我们将感激不尽。
-因为它们绝大部分是支持 APACHE + MIT 双协议的,因此我们选择了遵循其中的 MIT 协议,并在这里统一对借鉴的书籍进行说明。
+既然是开源,那最大的鼓励不是 money,而是 star:) **如果大家觉得这本书作者真的用心了,就帮我们点一个 🌟 吧,这将是我们继续前行最大的动力**
+
+> 在开源版权上,我们选择了 [No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB),这意味着读者可以随意的 fork 和阅读,但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,望理解。
+
+
+## 学习社区
+为了帮助大家更好的学习和交流 Rust,我们建立了一个社区:**Rust学习社区**。
-### 贡献者
+QQ群 1009730433, 欢迎大家加入,一起 happy,一起进步。
+
+
+## 贡献者
非常感谢本教程的所有贡献者们,正是有了你们,才有了现在的高质量 Rust 教程!
+- [@AllanDowney](https://github.com/AllanDowney)
- [@JesseAtSZ](https://github.com/JesseAtSZ)
-- [@mg-chao](https://github.com/mg-chao)
- [@1132719438](https://github.com/1132719438)
-- [@codemystery](https://github.com/codemystery)
-- [@AllanDowney](https://github.com/AllanDowney)
- [@Mintnoii](https://github.com/Mintnoii)
+- [@Rustln](https://github.com/rustln)
尤其感谢这些主要贡献者,谢谢你们花费大量时间贡献了多处`fix`和高质量的内容优化。非常感动,再次感谢~~
+## 借鉴的书籍
+
+站在巨人的肩膀上,能帮我们看的更远,特此感谢以下巨人:
+
+- [Rust Book](https://doc.rust-lang.org/book)
+- [Rust nomicon](https://doc.rust-lang.org/nomicon/intro.html)
+- [Async Rust](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html)
+- 详细清单参见 [这里](./assets/writing-material/books.md)
+
+因为它们绝大部分是支持 APACHE + MIT 双协议的,因此我们选择了遵循其中的 MIT 协议,并在这里统一对借鉴的书籍进行说明。
diff --git a/src/advance/circle-self-ref/circle-reference.md b/src/advance/circle-self-ref/circle-reference.md
index 6839f544..197c87ab 100644
--- a/src/advance/circle-self-ref/circle-reference.md
+++ b/src/advance/circle-self-ref/circle-reference.md
@@ -295,11 +295,11 @@ fn main() {
## unsafe 解决循环引用
-除了使用 Rust 标准库提供的这些类型,你还可以使用 `unsafe` 里的原生指针来解决这些棘手的问题,但是由于我们还没有讲解 `unsafe`,因此这里就不进行展开,只附上[源码链接](https://github.com/sunface/rust-algos/blob/fbcdccf3e8178a9039329562c0de0fd01a3372fb/src/unsafe/self-ref.md), 挺长的,需要耐心 o_o
+除了使用 Rust 标准库提供的这些类型,你还可以使用 `unsafe` 里的裸指针来解决这些棘手的问题,但是由于我们还没有讲解 `unsafe`,因此这里就不进行展开,只附上[源码链接](https://github.com/sunface/rust-algos/blob/fbcdccf3e8178a9039329562c0de0fd01a3372fb/src/unsafe/self-ref.md), 挺长的,需要耐心 o_o
虽然 `unsafe` 不安全,但是在各种库的代码中依然很常见用它来实现自引用结构,主要优点如下:
-- 性能高,毕竟直接用原生指针操作
+- 性能高,毕竟直接用裸指针操作
- 代码更简单更符合直觉: 对比下 `Option>>`
## 总结
diff --git a/src/advance/circle-self-ref/self-referential.md b/src/advance/circle-self-ref/self-referential.md
index 4daf9d08..11fedf63 100644
--- a/src/advance/circle-self-ref/self-referential.md
+++ b/src/advance/circle-self-ref/self-referential.md
@@ -160,9 +160,9 @@ fn main() {
}
```
-在这里,我们在 `pointer_to_value` 中直接存储原生指针,而不是 Rust 的引用,因此不再受到 Rust 借用规则和生命周期的限制,而且实现起来非常清晰、简洁。但是缺点就是,通过指针获取值时需要使用 `unsafe` 代码。
+在这里,我们在 `pointer_to_value` 中直接存储裸指针,而不是 Rust 的引用,因此不再受到 Rust 借用规则和生命周期的限制,而且实现起来非常清晰、简洁。但是缺点就是,通过指针获取值时需要使用 `unsafe` 代码。
-当然,上面的代码你还能通过原生指针来修改 `String`,但是需要将 `*const` 修改为 `*mut`:
+当然,上面的代码你还能通过裸指针来修改 `String`,但是需要将 `*const` 修改为 `*mut`:
```rust
#[derive(Debug)]
@@ -230,7 +230,7 @@ use std::ptr::NonNull;
// 下面是一个自引用数据结构体,因为 slice 字段是一个指针,指向了 data 字段
// 我们无法使用普通引用来实现,因为违背了 Rust 的编译规则
-// 因此,这里我们使用了一个原生指针,通过 NonNull 来确保它不会为 null
+// 因此,这里我们使用了一个裸指针,通过 NonNull 来确保它不会为 null
struct Unmovable {
data: String,
slice: NonNull,
@@ -272,7 +272,7 @@ fn main() {
上面的代码也非常清晰,虽然使用了 `unsafe`,其实更多的是无奈之举,跟之前的 `unsafe` 实现完全不可同日而语。
-其实 `Pin` 在这里并没有魔法,它也并不是实现自引用类型的主要原因,最关键的还是里面的原生指针的使用,而 `Pin` 起到的作用就是确保我们的值不会被移走,否则指针就会指向一个错误的地址!
+其实 `Pin` 在这里并没有魔法,它也并不是实现自引用类型的主要原因,最关键的还是里面的裸指针的使用,而 `Pin` 起到的作用就是确保我们的值不会被移走,否则指针就会指向一个错误的地址!
## 使用 ouroboros
diff --git a/src/advance/concurrency-with-threads/send-sync.md b/src/advance/concurrency-with-threads/send-sync.md
index 44dcdb27..1caf4bcc 100644
--- a/src/advance/concurrency-with-threads/send-sync.md
+++ b/src/advance/concurrency-with-threads/send-sync.md
@@ -1,6 +1,6 @@
# 基于 Send 和 Sync 的线程安全
-为何 Rc、RefCell 和原生指针不可以在多线程间使用?如何让原生指针可以在多线程使用?我们一起来探寻下这些问题的答案。
+为何 Rc、RefCell 和裸指针不可以在多线程间使用?如何让裸指针可以在多线程使用?我们一起来探寻下这些问题的答案。
## 无法用于多线程的`Rc`
@@ -27,7 +27,7 @@ error[E0277]: `Rc` cannot be sent between threads safely
= help: within `[closure@src/main.rs:5:27: 7:6]`, the trait `Send` is not implemented for `Rc`
```
-表面原因是`Rc`无法在线程间安全的转移,实际是编译器给予我们的那句帮助: `the trait Send is not implemented for Rc`(`Rc`未实现`Send`特征), 那么此处的`Send`特征又是何方神圣?
+表面原因是`Rc`无法在线程间安全的转移,实际是编译器给予我们的那句帮助: ```the trait `Send` is not implemented for `Rc` ```(`Rc`未实现`Send`特征), 那么此处的`Send`特征又是何方神圣?
## Rc 和 Arc 源码对比
@@ -50,7 +50,7 @@ unsafe impl Sync for Arc {}
`Send`和`Sync`是 Rust 安全并发的重中之重,但是实际上它们只是标记特征(marker trait,该特征未定义任何行为,因此非常适合用于标记), 来看看它们的作用:
- 实现`Send`的类型可以在线程间安全的传递其所有权
-- 实现了`Sync`的类型可以在线程间安全的共享(通过引用)
+- 实现`Sync`的类型可以在线程间安全的共享(通过引用)
这里还有一个潜在的依赖:一个类型要在线程间安全的共享的前提是,指向它的引用必须能在线程间传递。因为如果引用都不能被传递,我们就无法在多个线程间使用引用去访问同一个数据了。
@@ -62,7 +62,7 @@ unsafe impl Sync for Arc {}
unsafe impl Sync for RwLock {}
```
-首先`RwLock`可以在线程间安全的共享,那它肯定是实现了`Sync`,但是我们的关注点不在这里。众多周知,`RwLock`可以并发的读,说明其中的值`T`必定也可以在线程间共享,那`T`必定要实现`Sync`。
+首先`RwLock`可以在线程间安全的共享,那它肯定是实现了`Sync`,但是我们的关注点不在这里。众所周知,`RwLock`可以并发的读,说明其中的值`T`必定也可以在线程间共享,那`T`必定要实现`Sync`。
果不其然,上述代码中,`T`的特征约束中就有一个`Sync`特征,那问题又来了,`Mutex`是不是相反?再来看看:
@@ -80,19 +80,19 @@ unsafe impl Sync for Mutex {}
正是因为以上规则,Rust 中绝大多数类型都实现了`Send`和`Sync`,除了以下几个(事实上不止这几个,只不过它们比较常见):
-- 原生指针两者都没实现,因为它本身就没有任何安全保证
+- 裸指针两者都没实现,因为它本身就没有任何安全保证
- `UnsafeCell`不是`Sync`,因此`Cell`和`RefCell`也不是
- `Rc`两者都没实现(因为内部的引用计数器不是线程安全的)
-当然,如果是自定义的复合类型,那没实现那哥俩的就较为常见了:**只要复合类型中有一个成员不是`Send`或`Sync`,那么该符合类型也就不是`Send`或`Sync`**。
+当然,如果是自定义的复合类型,那没实现那哥俩的就较为常见了:**只要复合类型中有一个成员不是`Send`或`Sync`,那么该复合类型也就不是`Send`或`Sync`**。
**手动实现 `Send` 和 `Sync` 是不安全的**,通常并不需要手动实现 Send 和 Sync trait,实现者需要使用`unsafe`小心维护并发安全保证。
-至此,相关的概念大家已经掌握,但是我敢肯定,对于这两个滑不溜秋的家伙,大家依然会非常模糊,不知道它们该如何使用。那么我们来一起看看如何让原生指针可以在线程间安全的使用。
+至此,相关的概念大家已经掌握,但是我敢肯定,对于这两个滑不溜秋的家伙,大家依然会非常模糊,不知道它们该如何使用。那么我们来一起看看如何让裸指针可以在线程间安全的使用。
-## 为原生指针实现`Send`
+## 为裸指针实现`Send`
-上面我们提到原生指针既没实现`Send`,意味着下面代码会报错:
+上面我们提到裸指针既没实现`Send`,意味着下面代码会报错:
```rust
use std::thread;
@@ -106,7 +106,7 @@ fn main() {
}
```
-报错跟之前无二: `*mut u8 cannot be sent between threads safely`, 但是有一个问题,我们无法为其直接实现`Send`特征,好在可以用[`newtype`类型](../custom-type.md#newtype) :`struct MyBox(*mut u8);`。
+报错跟之前无二: ``` `*mut u8` cannot be sent between threads safely```, 但是有一个问题,我们无法为其直接实现`Send`特征,好在可以用[`newtype`类型](../into-types/custom-type.md#newtype) :`struct MyBox(*mut u8);`。
还记得之前的规则吗:复合类型中有一个成员没实现`Send`,该复合类型就不是`Send`,因此我们需要手动为它实现:
@@ -128,7 +128,7 @@ fn main() {
此时,我们的指针已经可以欢快的在多线程间撒欢,以上代码很简单,但有一点需要注意:`Send`和`Sync`是`unsafe`特征,实现时需要用`unsafe`代码块包裹。
-## 为原生指针实现`Sync`
+## 为裸指针实现`Sync`
由于`Sync`是多线程间共享一个值,大家可能会想这么实现:
@@ -188,9 +188,9 @@ unsafe impl Sync for MyBox {}
## 总结
-通过上面的两个原生指针的例子,我们了解了如何实现`Send`和`Sync`,以及如何只实现`Send`而不实现`Sync`,简单总结下:
+通过上面的两个裸指针的例子,我们了解了如何实现`Send`和`Sync`,以及如何只实现`Send`而不实现`Sync`,简单总结下:
1. 实现`Send`的类型可以在线程间安全的传递其所有权, 实现`Sync`的类型可以在线程间安全的共享(通过引用)
-2. 绝大部分类型都实现了`Send`和`Sync`,常见的未实现的有:原生指针、Cell/RefCell、Rc 等
+2. 绝大部分类型都实现了`Send`和`Sync`,常见的未实现的有:裸指针、`Cell`、`RefCell`、`Rc` 等
3. 可以为自定义类型实现`Send`和`Sync`,但是需要`unsafe`代码块
-4. 可以为部分 Rust 中的类型实现`Send`、`Sync`,但是需要使用`newtype`,例如文中的原生指针例子
+4. 可以为部分 Rust 中的类型实现`Send`、`Sync`,但是需要使用`newtype`,例如文中的裸指针例子
diff --git a/src/advance/concurrency-with-threads/sync1.md b/src/advance/concurrency-with-threads/sync1.md
index 182c6897..cec9baef 100644
--- a/src/advance/concurrency-with-threads/sync1.md
+++ b/src/advance/concurrency-with-threads/sync1.md
@@ -62,6 +62,24 @@ fn main() {
正因为智能指针的使用,使得我们无需任何操作就能获取其中的数据。 如果释放锁,你需要做的仅仅是做好锁的作用域管理,例如上述代码的内部花括号使用,建议读者尝试下去掉内部的花括号,然后再次尝试获取第二个锁`num1`,看看会发生什么,友情提示:不会报错,但是主线程会永远阻塞,因为不幸发生了死锁。
+```rust
+use std::sync::Mutex;
+
+fn main() {
+ let m = Mutex::new(5);
+
+ let mut num = m.lock().unwrap();
+ *num = 6;
+ // 锁还没有被 drop 就尝试申请下一个锁,导致主线程阻塞
+ // drop(num); // 手动 drop num ,可以让 num1 申请到下个锁
+ let mut num1 = m.lock().unwrap();
+ *num1 = 7;
+ // drop(num1); // 手动 drop num1 ,观察打印结果的不同
+
+ println!("m = {:?}", m);
+}
+```
+
#### 多线程中使用 Mutex
单线程中使用锁,说实话纯粹是为了演示功能,毕竟多线程才是锁的舞台。 现在,我们再来看看,如何在多线程下使用`Mutex`来访问同一个资源.
@@ -114,14 +132,14 @@ error[E0277]: `Rc>` cannot be sent between threads safely
// `Rc`无法在线程中安全的传输
--> src/main.rs:11:22
|
-11 | let handle = thread::spawn(move || {
+13 | let handle = thread::spawn(move || {
| ______________________^^^^^^^^^^^^^_-
| | |
| | `Rc>` cannot be sent between threads safely
-12 | | let mut num = counter.lock().unwrap();
-13 | |
-14 | | *num += 1;
-15 | | });
+14 | | let mut num = counter.lock().unwrap();
+15 | |
+16 | | *num += 1;
+17 | | });
| |_________- within this `[closure@src/main.rs:11:36: 15:10]`
|
= help: within `[closure@src/main.rs:11:36: 15:10]`, the trait `Send` is not implemented for `Rc>`
@@ -231,22 +249,22 @@ fn main() {
for _ in 0..1 {
// 线程1
if i_thread % 2 == 0 {
- // 锁住mutex1
+ // 锁住MUTEX1
let guard: MutexGuard = MUTEX1.lock().unwrap();
- println!("线程 {} 锁住了mutex1,接着准备去锁mutex2 !", i_thread);
+ println!("线程 {} 锁住了MUTEX1,接着准备去锁MUTEX2 !", i_thread);
- // 当前线程睡眠一小会儿,等待线程2锁住mutex2
+ // 当前线程睡眠一小会儿,等待线程2锁住MUTEX2
sleep(Duration::from_millis(10));
- // 去锁mutex2
+ // 去锁MUTEX2
let guard = MUTEX2.lock().unwrap();
// 线程2
} else {
- // 锁住mutex2
+ // 锁住MUTEX2
let _guard = MUTEX2.lock().unwrap();
- println!("线程 {} 锁住了mutex2, 准备去锁mutex1", i_thread);
+ println!("线程 {} 锁住了MUTEX2, 准备去锁MUTEX1", i_thread);
let _guard = MUTEX1.lock().unwrap();
}
@@ -265,9 +283,9 @@ fn main() {
在上面的描述中,我们用了"可能"二字,原因在于死锁在这段代码中不是必然发生的,总有一次运行你能看到最后一行打印输出。这是由于子线程的初始化顺序和执行速度并不确定,我们无法确定哪个线程中的锁先被执行,因此也无法确定两个线程对锁的具体使用顺序。
-但是,可以简单的说明下死锁发生的必然条件:线程 1 锁住了`mutex1`并且线程`2`锁住了`mutex2`,然后线程 1 试图去访问`mutex2`,同时线程`2`试图去访问`mutex1`,就会死锁。 因为线程 2 需要等待线程 1 释放`mutex1`后,才会释放`mutex2`,而与此同时,线程 1 需要等待线程 2 释放`mutex2`后才能释放`mutex1`,这种情况造成了两个线程都无法释放对方需要的锁,最终死锁。
+但是,可以简单的说明下死锁发生的必然条件:线程 1 锁住了`MUTEX1`并且线程`2`锁住了`MUTEX2`,然后线程 1 试图去访问`MUTEX2`,同时线程`2`试图去访问`MUTEX1`,就会死锁。 因为线程 2 需要等待线程 1 释放`MUTEX1`后,才会释放`MUTEX2`,而与此同时,线程 1 需要等待线程 2 释放`MUTEX2`后才能释放`MUTEX1`,这种情况造成了两个线程都无法释放对方需要的锁,最终死锁。
-那么为何某些时候,死锁不会发生?原因很简单,线程 2 在线程 1 锁`mutex1`之前,就已经全部执行完了,随之线程 2 的`mutex2`和`mutex1`被全部释放,线程 1 对锁的获取将不再有竞争者。 同理,线程 1 若全部被执行完,那线程 2 也不会被锁,因此我们在线程 1 中间加一个睡眠,增加死锁发生的概率。如果你在线程 2 中同样的位置也增加一个睡眠,那死锁将必然发生!
+那么为何某些时候,死锁不会发生?原因很简单,线程 2 在线程 1 锁`MUTEX1`之前,就已经全部执行完了,随之线程 2 的`MUTEX2`和`MUTEX1`被全部释放,线程 1 对锁的获取将不再有竞争者。 同理,线程 1 若全部被执行完,那线程 2 也不会被锁,因此我们在线程 1 中间加一个睡眠,增加死锁发生的概率。如果你在线程 2 中同样的位置也增加一个睡眠,那死锁将必然发生!
#### try_lock
@@ -292,26 +310,26 @@ fn main() {
for _ in 0..1 {
// 线程1
if i_thread % 2 == 0 {
- // 锁住mutex1
+ // 锁住MUTEX1
let guard: MutexGuard = MUTEX1.lock().unwrap();
- println!("线程 {} 锁住了mutex1,接着准备去锁mutex2 !", i_thread);
+ println!("线程 {} 锁住了MUTEX1,接着准备去锁MUTEX2 !", i_thread);
- // 当前线程睡眠一小会儿,等待线程2锁住mutex2
+ // 当前线程睡眠一小会儿,等待线程2锁住MUTEX2
sleep(Duration::from_millis(10));
- // 去锁mutex2
+ // 去锁MUTEX2
let guard = MUTEX2.try_lock();
- println!("线程1获取mutex2锁的结果: {:?}",guard);
+ println!("线程1获取MUTEX2锁的结果: {:?}",guard);
// 线程2
} else {
- // 锁住mutex2
+ // 锁住MUTEX2
let _guard = MUTEX2.lock().unwrap();
- println!("线程 {} 锁住了mutex2, 准备去锁mutex1", i_thread);
+ println!("线程 {} 锁住了MUTEX2, 准备去锁MUTEX1", i_thread);
sleep(Duration::from_millis(10));
let guard = MUTEX1.try_lock();
- println!("线程2获取mutex1锁的结果: {:?}",guard);
+ println!("线程2获取MUTEX1锁的结果: {:?}",guard);
}
}
}));
@@ -329,10 +347,10 @@ fn main() {
为了演示`try_lock`的作用,我们特定使用了之前必定会死锁的代码,并且将`lock`替换成`try_lock`,与之前的结果不同,这段代码将不会再有死锁发生:
```console
-线程 0 锁住了mutex1,接着准备去锁mutex2 !
-线程 1 锁住了mutex2, 准备去锁mutex1
-线程2获取mutex1锁的结果: Err("WouldBlock")
-线程1获取mutex2锁的结果: Ok(0)
+线程 0 锁住了MUTEX1,接着准备去锁MUTEX2 !
+线程 1 锁住了MUTEX2, 准备去锁MUTEX1
+线程2获取MUTEX1锁的结果: Err("WouldBlock")
+线程1获取MUTEX2锁的结果: Ok(0)
死锁没有发生
```
diff --git a/src/advance/concurrency-with-threads/sync2.md b/src/advance/concurrency-with-threads/sync2.md
index 0b9af5fc..92fdb126 100644
--- a/src/advance/concurrency-with-threads/sync2.md
+++ b/src/advance/concurrency-with-threads/sync2.md
@@ -166,7 +166,7 @@ Y = 3; Y *= 2;
X = 2; }
```
-还是可能出现`Y=2`,因为`Main`线程中的`X`和`Y`被同步到其它 CPU 缓存中的顺序未必一致。
+还是可能出现`Y = 2`,因为`Main`线程中的`X`和`Y`被同步到其它 CPU 缓存中的顺序未必一致。
#### 限定内存顺序的 5 个规则
diff --git a/src/confonding/cow.md b/src/advance/confonding/cow.md
similarity index 100%
rename from src/confonding/cow.md
rename to src/advance/confonding/cow.md
diff --git a/src/confonding/eq.md b/src/advance/confonding/eq.md
similarity index 100%
rename from src/confonding/eq.md
rename to src/advance/confonding/eq.md
diff --git a/src/confonding/intro.md b/src/advance/confonding/intro.md
similarity index 100%
rename from src/confonding/intro.md
rename to src/advance/confonding/intro.md
diff --git a/src/confonding/lifetime.md b/src/advance/confonding/lifetime.md
similarity index 100%
rename from src/confonding/lifetime.md
rename to src/advance/confonding/lifetime.md
diff --git a/src/confonding/move-copy.md b/src/advance/confonding/move-copy.md
similarity index 100%
rename from src/confonding/move-copy.md
rename to src/advance/confonding/move-copy.md
diff --git a/src/advance/confonding/pointer.md b/src/advance/confonding/pointer.md
new file mode 100644
index 00000000..e07a9fcb
--- /dev/null
+++ b/src/advance/confonding/pointer.md
@@ -0,0 +1 @@
+# 裸指针、引用和智能指针 todo
diff --git a/src/confonding/slice.md b/src/advance/confonding/slice.md
similarity index 100%
rename from src/confonding/slice.md
rename to src/advance/confonding/slice.md
diff --git a/src/confonding/string.md b/src/advance/confonding/string.md
similarity index 100%
rename from src/confonding/string.md
rename to src/advance/confonding/string.md
diff --git a/src/advance/lifetime/advance.md b/src/advance/lifetime/advance.md
index d00bb683..014fdf5c 100644
--- a/src/advance/lifetime/advance.md
+++ b/src/advance/lifetime/advance.md
@@ -136,7 +136,7 @@ error[E0499]: cannot borrow `*map` as mutable more than once at a time
不安全代码(`unsafe`)经常会凭空产生引用或生命周期,这些生命周期被称为是 **无界(unbound)** 的。
-无界生命周期往往是在解引用一个原生指针(裸指针 raw pointer)时产生的,换句话说,它是凭空产生的,因为输入参数根本就没有这个生命周期:
+无界生命周期往往是在解引用一个裸指针(裸指针 raw pointer)时产生的,换句话说,它是凭空产生的,因为输入参数根本就没有这个生命周期:
```rust
fn f<'a, T>(x: *const T) -> &'a T {
diff --git a/src/advance/lifetime/static.md b/src/advance/lifetime/static.md
index 89607cbb..619aae5f 100644
--- a/src/advance/lifetime/static.md
+++ b/src/advance/lifetime/static.md
@@ -57,7 +57,7 @@ fn get_memory_location() -> (usize, usize) {
}
fn get_str_at_location(pointer: usize, length: usize) -> &'static str {
- // 使用原生指针需要 `unsafe{}` 语句块
+ // 使用裸指针需要 `unsafe{}` 语句块
unsafe { from_utf8_unchecked(from_raw_parts(pointer as *const u8, length)) }
}
@@ -68,7 +68,7 @@ fn main() {
"The {} bytes at 0x{:X} stored: {}",
length, pointer, message
);
- // 如果大家想知道为何处理原生指针需要 `unsafe`,可以试着反注释以下代码
+ // 如果大家想知道为何处理裸指针需要 `unsafe`,可以试着反注释以下代码
// let message = get_str_at_location(1000, 10);
}
```
diff --git a/src/advance/macro.md b/src/advance/macro.md
index 1843a754..230978da 100644
--- a/src/advance/macro.md
+++ b/src/advance/macro.md
@@ -320,7 +320,7 @@ hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
# hello_macro_derive = { path = "./hello_macro_derive" }
```
-此时,`hello_macro` 项目就可以成功的引用到 `hello_macro_derive` 本地包了,对于项目依赖引入的详细介绍,可以参见 [Cargo 章节](https://course.rs/cargo/dependency.html)。
+此时,`hello_macro` 项目就可以成功的引用到 `hello_macro_derive` 本地包了,对于项目依赖引入的详细介绍,可以参见 [Cargo 章节](https://course.rs/toolchains/cargo/dependency.html)。
接下来,就到了重头戏环节,一起来看看该如何定义过程宏。
diff --git a/src/advance/unsafe/intro.md b/src/advance/unsafe/intro.md
index 28d5cd57..f13e16e7 100644
--- a/src/advance/unsafe/intro.md
+++ b/src/advance/unsafe/intro.md
@@ -42,17 +42,17 @@ fn main() {
}
```
-上面代码中, `r1` 是一个原生指针(又称裸指针,raw pointer),由于它具有破坏 Rust 内存安全的潜力,因此只能在 `unsafe` 代码块中使用,如果你去掉 `unsafe {}`,编译器会立刻报错。
+上面代码中, `r1` 是一个裸指针(又称裸指针,raw pointer),由于它具有破坏 Rust 内存安全的潜力,因此只能在 `unsafe` 代码块中使用,如果你去掉 `unsafe {}`,编译器会立刻报错。
言归正传, `unsafe` 能赋予我们 5 种超能力,这些能力在安全的 Rust 代码中是无法获取的:
-- 解引用原生指针,就如上例所示
+- 解引用裸指针,就如上例所示
- 调用一个 `unsafe` 或外部的函数
- 访问或修改一个可变的[静态变量](https://course.rs/advance/global-variable.html#静态变量)
- 实现一个 `unsafe` 特征
- 访问 `union` 中的字段
-在本章中,我们将着重讲解原生指针和 FFI 的使用。
+在本章中,我们将着重讲解裸指针和 FFI 的使用。
## unsafe 的安全保证
@@ -60,7 +60,7 @@ fn main() {
首先,`unsafe` 并不能绕过 Rust 的借用检查,也不能关闭任何 Rust 的安全检查规则,例如当你在 `unsafe` 中使用**引用**时,该有的检查一样都不会少。
-因此 `unsafe` 能给大家提供的也仅仅是之前的 5 种超能力,在使用这 5 种能力时,编译器才不会进行内存安全方面的检查,最典型的就是使用**原生指针**(引用和原生指针有很大的区别)。
+因此 `unsafe` 能给大家提供的也仅仅是之前的 5 种超能力,在使用这 5 种能力时,编译器才不会进行内存安全方面的检查,最典型的就是使用**裸指针**(引用和裸指针有很大的区别)。
## 谈虎色变?
diff --git a/src/advance/unsafe/superpowers.md b/src/advance/unsafe/superpowers.md
index 5be4d80b..aa14868b 100644
--- a/src/advance/unsafe/superpowers.md
+++ b/src/advance/unsafe/superpowers.md
@@ -2,24 +2,24 @@
古龙有一部小说,名为"七种兵器",其中每一种都精妙绝伦,令人闻风丧胆,而 `unsafe` 也有五种兵器,它们可以让你拥有其它代码无法实现的能力,同时它们也像七种兵器一样令人闻风丧胆,下面一起来看看庐山真面目。
-## 解引用原生指针
+## 解引用裸指针
-原生指针(raw pointer) 又称裸指针,在功能上跟引用类似,同时它也需要显式地注明可变性。但是又和引用有所不同,原生指针长这样: `*const T` 和 `*mut T`,它们分别代表了不可变和可变。
+裸指针(raw pointer) 又称裸指针,在功能上跟引用类似,同时它也需要显式地注明可变性。但是又和引用有所不同,裸指针长这样: `*const T` 和 `*mut T`,它们分别代表了不可变和可变。
-大家在之前学过 `*` 操作符,知道它可以用于解引用,但是在原生指针 `*const T` 中,这里的 `*` 只是类型名称的一部分,并没有解引用的含义。
+大家在之前学过 `*` 操作符,知道它可以用于解引用,但是在裸指针 `*const T` 中,这里的 `*` 只是类型名称的一部分,并没有解引用的含义。
-至此,我们已经学过三种类似指针的概念:引用、智能指针和原生指针。与前两者不同,原生指针:
+至此,我们已经学过三种类似指针的概念:引用、智能指针和裸指针。与前两者不同,裸指针:
- 可以绕过 Rust 的借用规则,可以同时拥有一个数据的可变、不可变指针,甚至还能拥有多个可变的指针
- 并不能保证指向合法的内存
- 可以是 `null`
- 没有实现任何自动的回收 (drop)
-总之,原生指针跟 C 指针是非常像的,使用它需要以牺牲安全性为前提,但我们获得了更好的性能,也可以跟其它语言或硬件打交道。
+总之,裸指针跟 C 指针是非常像的,使用它需要以牺牲安全性为前提,但我们获得了更好的性能,也可以跟其它语言或硬件打交道。
-#### 基于引用创建原生指针
+#### 基于引用创建裸指针
-下面的代码**基于值的引用**同时创建了可变和不可变的原生指针:
+下面的代码**基于值的引用**同时创建了可变和不可变的裸指针:
```rust
let mut num = 5;
@@ -28,9 +28,9 @@ let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
```
-`as` 可以用于强制类型转换,在[之前章节](https://course.rs/basic/converse.html)中有讲解。在这里,我们将引用 `&num / &mut num` 强转为相应的原生指针 `*const i32 / *mut i32`。
+`as` 可以用于强制类型转换,在[之前章节](https://course.rs/basic/converse.html)中有讲解。在这里,我们将引用 `&num / &mut num` 强转为相应的裸指针 `*const i32 / *mut i32`。
-细心的同学可能会发现,在这段代码中并没有 `unsafe` 的身影,原因在于:**创建原生指针是安全的行为,而解引用原生指针才是不安全的行为** :
+细心的同学可能会发现,在这段代码中并没有 `unsafe` 的身影,原因在于:**创建裸指针是安全的行为,而解引用裸指针才是不安全的行为** :
```rust
fn main() {
@@ -44,16 +44,16 @@ fn main() {
}
```
-#### 基于内存地址创建原生指针
+#### 基于内存地址创建裸指针
-在上面例子中,我们基于现有的引用来创建原生指针,这种行为是很安全的。但是接下来的方式就不安全了:
+在上面例子中,我们基于现有的引用来创建裸指针,这种行为是很安全的。但是接下来的方式就不安全了:
```rust
let address = 0x012345usize;
let r = address as *const i32;
```
-这里基于一个内存地址来创建原生指针,可以想像,这种行为是相当危险的。试图使用任意的内存地址往往是一种未定义的行为(undefined behavior),因为该内存地址有可能存在值,也有可能没有,就算有值,也大概率不是你需要的值。
+这里基于一个内存地址来创建裸指针,可以想像,这种行为是相当危险的。试图使用任意的内存地址往往是一种未定义的行为(undefined behavior),因为该内存地址有可能存在值,也有可能没有,就算有值,也大概率不是你需要的值。
同时编译器也有可能会优化这段代码,会造成没有任何内存访问发生,甚至程序还可能发生段错误(segmentation fault)。**总之,你几乎没有好的理由像上面这样实现代码,虽然它是可行的**。
@@ -82,7 +82,7 @@ fn main() {
"The {} bytes at 0x{:X} stored: {}",
length, pointer, message
);
- // 如果大家想知道为何处理原生指针需要 `unsafe`,可以试着反注释以下代码
+ // 如果大家想知道为何处理裸指针需要 `unsafe`,可以试着反注释以下代码
// let message = get_str_at_location(1000, 10);
}
```
@@ -100,13 +100,13 @@ unsafe {
}
```
-使用 `*` 可以对原生指针进行解引用,由于该指针的内存安全性并没有任何保证,因此我们需要使用 `unsafe` 来包裹解引用的逻辑(切记,`unsafe` 语句块的范围一定要尽可能的小,具体原因在上一章节有讲)。
+使用 `*` 可以对裸指针进行解引用,由于该指针的内存安全性并没有任何保证,因此我们需要使用 `unsafe` 来包裹解引用的逻辑(切记,`unsafe` 语句块的范围一定要尽可能的小,具体原因在上一章节有讲)。
-以上代码另一个值得注意的点就是:除了使用 `as` 来显式的转换,我们还使用了隐式的转换方式 `let c: *const i32 = &a;`。在实际使用中,我们建议使用 `as` 来转换,因为这种显式的方式更有助于提醒用户:你在使用的指针是原生指针,需要小心。
+以上代码另一个值得注意的点就是:除了使用 `as` 来显式的转换,我们还使用了隐式的转换方式 `let c: *const i32 = &a;`。在实际使用中,我们建议使用 `as` 来转换,因为这种显式的方式更有助于提醒用户:你在使用的指针是裸指针,需要小心。
-#### 基于智能指针创建原生指针
+#### 基于智能指针创建裸指针
-还有一种创建原生指针的方式,那就是基于智能指针来创建:
+还有一种创建裸指针的方式,那就是基于智能指针来创建:
```rust
let a: Box = Box::new(10);
@@ -118,9 +118,9 @@ let c: *const i32 = Box::into_raw(a);
#### 小结
-像之前代码演示的那样,使用原生指针可以让我们创建两个可变指针都指向同一个数据,如果使用安全的 Rust,你是无法做到这一点的,违背了借用规则,编译器会对我们进行无情的阻止。因此原生指针可以绕过借用规则,但是由此带来的数据竞争问题,就需要大家自己来处理了,总之,需要小心!
+像之前代码演示的那样,使用裸指针可以让我们创建两个可变指针都指向同一个数据,如果使用安全的 Rust,你是无法做到这一点的,违背了借用规则,编译器会对我们进行无情的阻止。因此裸指针可以绕过借用规则,但是由此带来的数据竞争问题,就需要大家自己来处理了,总之,需要小心!
-既然这么危险,为何还要使用原生指针?除了之前提到的性能等原因,还有一个重要用途就是跟 `C` 语言的代码进行交互( FFI ),在讲解 FFI 之前,先来看看如何调用 unsafe 函数或方法。
+既然这么危险,为何还要使用裸指针?除了之前提到的性能等原因,还有一个重要用途就是跟 `C` 语言的代码进行交互( FFI ),在讲解 FFI 之前,先来看看如何调用 unsafe 函数或方法。
## 调用 unsafe 函数或方法
@@ -223,13 +223,13 @@ fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
相比安全实现,这段代码就显得没那么好理解了,甚至于我们还需要像 C 语言那样,通过指针地址的偏移去控制数组的分割。
-- `as_mut_ptr` 会返回指向 `slice` 首地址的原生指针 `*mut i32`
+- `as_mut_ptr` 会返回指向 `slice` 首地址的裸指针 `*mut i32`
- `slice::from_raw_parts_mut` 函数通过指针和长度来创建一个新的切片,简单来说,该切片的初始地址是 `ptr`,长度为 `mid`
- `ptr.add(mid)` 可以获取第二个切片的初始地址,由于切片中的元素是 `i32` 类型,每个元素都占用了 4 个字节的内存大小,因此我们不能简单的用 `ptr + mid` 来作为初始地址,而应该使用 `ptr + 4 * mid`,但是这种使用方式并不安全,因此 `.add` 方法是最佳选择
-由于 `slice::from_raw_parts_mut` 使用原生指针作为参数,因此它是一个 `unsafe fn`,我们在使用它时,就必须用 `unsafe` 语句块进行包裹,类似的,`.add` 方法也是如此(还是那句话,不要将无关的代码包含在 `unsafe` 语句块中)。
+由于 `slice::from_raw_parts_mut` 使用裸指针作为参数,因此它是一个 `unsafe fn`,我们在使用它时,就必须用 `unsafe` 语句块进行包裹,类似的,`.add` 方法也是如此(还是那句话,不要将无关的代码包含在 `unsafe` 语句块中)。
-部分同学可能会有疑问,那这段代码我们怎么保证 `unsafe` 中使用的原生指针 `ptr` 和 `ptr.add(mid)` 是合法的呢?秘诀就在于 `assert!(mid <= len);` ,通过这个断言,我们保证了原生指针一定指向了 `slice` 切片中的某个元素,而不是一个莫名其妙的内存地址。
+部分同学可能会有疑问,那这段代码我们怎么保证 `unsafe` 中使用的裸指针 `ptr` 和 `ptr.add(mid)` 是合法的呢?秘诀就在于 `assert!(mid <= len);` ,通过这个断言,我们保证了裸指针一定指向了 `slice` 切片中的某个元素,而不是一个莫名其妙的内存地址。
再回到我们的主题:**虽然 split_at_mut 使用了 `unsafe`,但我们无需将其声明为 `unsafe fn`**,这种情况下就是使用安全的抽象包裹 `unsafe` 代码,这里的 `unsafe` 使用是非常安全的,因为我们从合法数据中创建了的合法指针。
@@ -315,7 +315,7 @@ pub extern "C" fn call_from_c() {
## 实现 unsafe 特征
-说实话,`unsafe` 的特征确实不多见,如果大家还记得的话,我们在之前的 [Send 和 Sync](https://course.rs/advance/concurrency-with-threads/send-sync.html#为原生指针实现sync) 章节中实现过 `unsafe` 特征 `Send`。
+说实话,`unsafe` 的特征确实不多见,如果大家还记得的话,我们在之前的 [Send 和 Sync](https://course.rs/advance/concurrency-with-threads/send-sync.html#为裸指针实现sync) 章节中实现过 `unsafe` 特征 `Send`。
之所以会有 `unsafe` 的特征,是因为该特征至少有一个方法包含有编译器无法验证的内容。`unsafe` 特征的声明很简单:
@@ -333,7 +333,7 @@ fn main() {}
通过 `unsafe impl` 的使用,我们告诉编译器:相应的正确性由我们自己来保证。
-再回到刚提到的 `Send` 特征,若我们的类型中的所有字段都实现了 `Send` 特征,那该类型也会自动实现 `Send`。但是如果我们想要为某个类型手动实现 `Send` ,例如为原生指针,那么就必须使用 `unsafe`,相关的代码在之前的链接中也有,大家可以移步查看。
+再回到刚提到的 `Send` 特征,若我们的类型中的所有字段都实现了 `Send` 特征,那该类型也会自动实现 `Send`。但是如果我们想要为某个类型手动实现 `Send` ,例如为裸指针,那么就必须使用 `unsafe`,相关的代码在之前的链接中也有,大家可以移步查看。
总之,`Send` 特征标记为 `unsafe` 是因为 Rust 无法验证我们的类型是否能在线程间安全的传递,因此就需要通过 `unsafe` 来告诉编译器,它无需操心,剩下的交给我们自己来处理。
diff --git a/src/appendix/keywords.md b/src/appendix/keywords.md
index 2f53b443..70f33010 100644
--- a/src/appendix/keywords.md
+++ b/src/appendix/keywords.md
@@ -26,7 +26,7 @@
- `match` - 模式匹配
- `mod` - 定义一个模块
- `move` - 使闭包获取其所捕获项的所有权
-- `mut` - 在引用、原生指针或模式绑定中使用,表明变量是可变的
+- `mut` - 在引用、裸指针或模式绑定中使用,表明变量是可变的
- `pub` - 表示结构体字段、`impl` 块或模块的公共可见性
- `ref` - 通过引用绑定
- `return` - 从函数中返回
diff --git a/src/appendix/operators.md b/src/appendix/operators.md
index 6db959ac..de57084c 100644
--- a/src/appendix/operators.md
+++ b/src/appendix/operators.md
@@ -25,7 +25,7 @@
| `*` | `expr * expr` | 算术乘法 | `Mul` |
| `*=` | `var *= expr` | 算术乘法与赋值 | `MulAssign` |
| `*` | `*expr` | 解引用 | |
-| `*` | `*const type`, `*mut type` | 原生指针 | |
+| `*` | `*const type`, `*mut type` | 裸指针 | |
| `+` | `trait + trait`, `'a + trait` | 复合类型限制 | |
| `+` | `expr + expr` | 算术加法 | `Add` |
| `+=` | `var += expr` | 算术加法与赋值 | `AddAssign` |
diff --git a/src/async/async-await.md b/src/async-rust/async/async-await.md
similarity index 100%
rename from src/async/async-await.md
rename to src/async-rust/async/async-await.md
diff --git a/src/async/future-excuting.md b/src/async-rust/async/future-excuting.md
similarity index 100%
rename from src/async/future-excuting.md
rename to src/async-rust/async/future-excuting.md
diff --git a/src/async/getting-started.md b/src/async-rust/async/getting-started.md
similarity index 100%
rename from src/async/getting-started.md
rename to src/async-rust/async/getting-started.md
diff --git a/src/async-rust/async/intro.md b/src/async-rust/async/intro.md
new file mode 100644
index 00000000..0b55ac86
--- /dev/null
+++ b/src/async-rust/async/intro.md
@@ -0,0 +1,5 @@
+# 异步编程
+
+接下来,我们将深入了解 async/await 的使用方式及背后的原理。
+
+> 本章在内容上大量借鉴和翻译了原版英文书籍[Asynchronous Programming In Rust](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html), 特此感谢
diff --git a/src/async/multi-futures-simultaneous.md b/src/async-rust/async/multi-futures-simultaneous.md
similarity index 100%
rename from src/async/multi-futures-simultaneous.md
rename to src/async-rust/async/multi-futures-simultaneous.md
diff --git a/src/async/pain-points-and-workarounds.md b/src/async-rust/async/pain-points-and-workarounds.md
similarity index 100%
rename from src/async/pain-points-and-workarounds.md
rename to src/async-rust/async/pain-points-and-workarounds.md
diff --git a/src/async/pin-unpin.md b/src/async-rust/async/pin-unpin.md
similarity index 97%
rename from src/async/pin-unpin.md
rename to src/async-rust/async/pin-unpin.md
index c2752f6b..8932155a 100644
--- a/src/async/pin-unpin.md
+++ b/src/async-rust/async/pin-unpin.md
@@ -16,7 +16,7 @@ struct SelfRef {
}
```
-在上面的结构体中,`pointer_to_value` 是一个原生指针,指向第一个字段 `value` 持有的字符串 `String` 。很简单对吧?现在考虑一个情况, 若`String` 被移动了怎么办?
+在上面的结构体中,`pointer_to_value` 是一个裸指针,指向第一个字段 `value` 持有的字符串 `String` 。很简单对吧?现在考虑一个情况, 若`String` 被移动了怎么办?
此时一个致命的问题就出现了:新的字符串的内存地址变了,而 `pointer_to_value` 依然指向之前的地址,一个重大 bug 就出现了!
@@ -168,7 +168,7 @@ impl Test {
}
```
-`Test` 提供了方法用于获取字段 `a` 和 `b` 的值的引用。这里`b` 是 `a` 的一个引用,但是我们并没有使用引用类型而是用了原生指针,原因是:Rust 的借用规则不允许我们这样用,因为不符合生命周期的要求。 此时的 `Test` 就是一个自引用结构体。
+`Test` 提供了方法用于获取字段 `a` 和 `b` 的值的引用。这里`b` 是 `a` 的一个引用,但是我们并没有使用引用类型而是用了裸指针,原因是:Rust 的借用规则不允许我们这样用,因为不符合生命周期的要求。 此时的 `Test` 就是一个自引用结构体。
如果不移动任何值,那么上面的例子将没有任何问题,例如:
diff --git a/src/async/web-server.md b/src/async-rust/async/web-server.md
similarity index 100%
rename from src/async/web-server.md
rename to src/async-rust/async/web-server.md
diff --git a/src/async/intro.md b/src/async-rust/intro.md
similarity index 57%
rename from src/async/intro.md
rename to src/async-rust/intro.md
index a888f990..416c5846 100644
--- a/src/async/intro.md
+++ b/src/async-rust/intro.md
@@ -1,6 +1,5 @@
-# 异步编程
-
-在艰难的学完 Rust 入门和进阶所有的 55 个章节后,我们终于来到了这里。假如之前攀登的是珠穆拉玛峰,那么现在攀登的就是乔戈里峰( 比珠峰还难攀爬... ),本章将学习的内容是关于 async 异步编程。
+# Rust 异步编程
+在艰难的学完 Rust 入门和进阶所有的 70 个章节后,我们终于来到了这里。假如之前攀登的是珠穆拉玛峰,那么现在攀登的就是乔戈里峰( 比珠峰还难攀爬... )。
如果你想开发 Web 服务器、数据库驱动、消息服务等需要高并发的服务,那么本章的内容将值得认真对待和学习,将从以下方面深入讲解 Rust 的异步编程:
@@ -9,6 +8,4 @@
- async/await 和 Pin/Unpin
- 异步编程常用的三方库
- tokio 库
-- 一些示例
-
-> 本章在内容上大量借鉴和翻译了原版英文书籍[Asynchronous Programming In Rust](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html), 特此感谢
+- 一些示例
\ No newline at end of file
diff --git a/src/tokio/async.md b/src/async-rust/tokio/async.md
similarity index 100%
rename from src/tokio/async.md
rename to src/async-rust/tokio/async.md
diff --git a/src/tokio/bridging-with-sync.md b/src/async-rust/tokio/bridging-with-sync.md
similarity index 100%
rename from src/tokio/bridging-with-sync.md
rename to src/async-rust/tokio/bridging-with-sync.md
diff --git a/src/tokio/channels.md b/src/async-rust/tokio/channels.md
similarity index 100%
rename from src/tokio/channels.md
rename to src/async-rust/tokio/channels.md
diff --git a/src/tokio/frame.md b/src/async-rust/tokio/frame.md
similarity index 100%
rename from src/tokio/frame.md
rename to src/async-rust/tokio/frame.md
diff --git a/src/tokio/getting-startted.md b/src/async-rust/tokio/getting-startted.md
similarity index 98%
rename from src/tokio/getting-startted.md
rename to src/async-rust/tokio/getting-startted.md
index d2459f01..f24cb882 100644
--- a/src/tokio/getting-startted.md
+++ b/src/async-rust/tokio/getting-startted.md
@@ -209,7 +209,7 @@ fn main() {
tokio = { version = "1", features = ["full"] }
```
-里面有个 `features = ["full"]` 可能大家会比较迷惑,当然,关于它的具体解释在本书的 [Cargo 详解专题](https://course.rs/cargo/intro.html) 有介绍,这里就简单进行说明,
+里面有个 `features = ["full"]` 可能大家会比较迷惑,当然,关于它的具体解释在本书的 [Cargo 详解专题](https://course.rs/toolchains/cargo/intro.html) 有介绍,这里就简单进行说明,
`Tokio` 有很多功能和特性,例如 `TCP`,`UDP`,`Unix sockets`,同步工具,多调度类型等等,不是每个应用都需要所有的这些特性。为了优化编译时间和最终生成可执行文件大小、内存占用大小,应用可以对这些特性进行可选引入。
diff --git a/src/tokio/graceful-shutdown.md b/src/async-rust/tokio/graceful-shutdown.md
similarity index 100%
rename from src/tokio/graceful-shutdown.md
rename to src/async-rust/tokio/graceful-shutdown.md
diff --git a/src/tokio/intro.md b/src/async-rust/tokio/intro.md
similarity index 100%
rename from src/tokio/intro.md
rename to src/async-rust/tokio/intro.md
diff --git a/src/tokio/io.md b/src/async-rust/tokio/io.md
similarity index 100%
rename from src/tokio/io.md
rename to src/async-rust/tokio/io.md
diff --git a/src/tokio/overview.md b/src/async-rust/tokio/overview.md
similarity index 100%
rename from src/tokio/overview.md
rename to src/async-rust/tokio/overview.md
diff --git a/src/tokio/select.md b/src/async-rust/tokio/select.md
similarity index 100%
rename from src/tokio/select.md
rename to src/async-rust/tokio/select.md
diff --git a/src/tokio/shared-state.md b/src/async-rust/tokio/shared-state.md
similarity index 100%
rename from src/tokio/shared-state.md
rename to src/async-rust/tokio/shared-state.md
diff --git a/src/tokio/spawning.md b/src/async-rust/tokio/spawning.md
similarity index 100%
rename from src/tokio/spawning.md
rename to src/async-rust/tokio/spawning.md
diff --git a/src/tokio/stream.md b/src/async-rust/tokio/stream.md
similarity index 100%
rename from src/tokio/stream.md
rename to src/async-rust/tokio/stream.md
diff --git a/src/basic/converse.md b/src/basic/converse.md
index 6ff7a47c..6d2f6a0b 100644
--- a/src/basic/converse.md
+++ b/src/basic/converse.md
@@ -282,11 +282,11 @@ impl Clone for Container {
你以为你之前凝视的是深渊吗?不,你凝视的只是深渊的大门。 `mem::transmute_copy` 才是真正的深渊,它比之前的还要更加危险和不安全。它从 `T` 类型中拷贝出 `U` 类型所需的字节数,然后转换成 `U`。 `mem::transmute` 尚有大小检查,能保证两个数据的内存大小一致,现在这哥们干脆连这个也丢了,只不过 `U` 的尺寸若是比 `T` 大,会是一个未定义行为。
-当然,你也可以通过原生指针转换和 `unions` (todo!)获得所有的这些功能,但是你将无法获得任何编译提示或者检查。原生指针转换和 `unions` 也不是魔法,无法逃避上面说的规则。
+当然,你也可以通过裸指针转换和 `unions` (todo!)获得所有的这些功能,但是你将无法获得任何编译提示或者检查。裸指针转换和 `unions` 也不是魔法,无法逃避上面说的规则。
`transmute` 虽然危险,但作为一本工具书,知识当然要全面,下面列举两个有用的 `transmute` 应用场景 :)。
-- 将原生指针变成函数指针:
+- 将裸指针变成函数指针:
```rust
fn foo() -> i32 {
@@ -295,7 +295,7 @@ fn foo() -> i32 {
let pointer = foo as *const ();
let function = unsafe {
- // 将原生指针转换为函数指针
+ // 将裸指针转换为函数指针
std::mem::transmute::<*const (), fn() -> i32>(pointer)
};
assert_eq!(function(), 0);
diff --git a/src/confonding/pointer.md b/src/confonding/pointer.md
deleted file mode 100644
index 146132f8..00000000
--- a/src/confonding/pointer.md
+++ /dev/null
@@ -1 +0,0 @@
-# 原生指针、引用和智能指针 todo
diff --git a/src/errorindex/borrowing/intro.md b/src/errorindex/borrowing/intro.md
deleted file mode 100644
index fcc4d744..00000000
--- a/src/errorindex/borrowing/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# 所有权和借用
diff --git a/src/errorindex/intro.md b/src/errorindex/intro.md
deleted file mode 100644
index 7258196c..00000000
--- a/src/errorindex/intro.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# 复杂错误索引
-
-读者可以在本章中通过错误前缀来索引查询相应的解决方案,简单的错误并不在本章的内容范畴之内。
\ No newline at end of file
diff --git a/src/errorindex/lifetime/intro.md b/src/errorindex/lifetime/intro.md
deleted file mode 100644
index b859823e..00000000
--- a/src/errorindex/lifetime/intro.md
+++ /dev/null
@@ -1 +0,0 @@
-# 生命周期 todo
diff --git a/src/first-try/cargo.md b/src/first-try/cargo.md
index a008da22..1592a553 100644
--- a/src/first-try/cargo.md
+++ b/src/first-try/cargo.md
@@ -161,10 +161,10 @@ color = { git = "https://github.com/bjz/color-rs" }
geometry = { path = "crates/geometry" }
```
-相信聪明的读者已经能看懂该如何引入外部依赖库,这里就不再赘述。详细的说明参见此章:[Cargo 依赖管理](https://course.rs/cargo/reference/specify-deps.html),但是不建议大家现在去看,只要按照目录浏览,拨云见雾指日可待。
+相信聪明的读者已经能看懂该如何引入外部依赖库,这里就不再赘述。详细的说明参见此章:[Cargo 依赖管理](https://course.rs/toolchains/cargo/reference/specify-deps.html),但是不建议大家现在去看,只要按照目录浏览,拨云见雾指日可待。
## 基于 cargo 的项目组织结构
-前文有提到 `cargo` 默认生成的项目结构,真实的项目肯定会有所不同,但是在目前的学习阶段,还无需关注。感兴趣的同学可以移步:[Cargo 项目结构](https://course.rs/cargo/guide/package-layout.html )
+前文有提到 `cargo` 默认生成的项目结构,真实的项目肯定会有所不同,但是在目前的学习阶段,还无需关注。感兴趣的同学可以移步:[Cargo 项目结构](https://course.rs/toolchains/cargo/guide/package-layout.html)
至此,大家对 Rust 项目的创建和管理已经有了初步的了解,那么来完善刚才的`"世界,你好"`项目吧。
diff --git a/src/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md b/src/practice/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md
similarity index 100%
rename from src/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md
rename to src/practice/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md
diff --git a/src/fight-with-compiler/borrowing/intro.md b/src/practice/fight-with-compiler/borrowing/intro.md
similarity index 100%
rename from src/fight-with-compiler/borrowing/intro.md
rename to src/practice/fight-with-compiler/borrowing/intro.md
diff --git a/src/fight-with-compiler/borrowing/ref-exist-in-out-fn.md b/src/practice/fight-with-compiler/borrowing/ref-exist-in-out-fn.md
similarity index 100%
rename from src/fight-with-compiler/borrowing/ref-exist-in-out-fn.md
rename to src/practice/fight-with-compiler/borrowing/ref-exist-in-out-fn.md
diff --git a/src/fight-with-compiler/intro.md b/src/practice/fight-with-compiler/intro.md
similarity index 100%
rename from src/fight-with-compiler/intro.md
rename to src/practice/fight-with-compiler/intro.md
diff --git a/src/fight-with-compiler/lifetime/closure-with-static.md b/src/practice/fight-with-compiler/lifetime/closure-with-static.md
similarity index 100%
rename from src/fight-with-compiler/lifetime/closure-with-static.md
rename to src/practice/fight-with-compiler/lifetime/closure-with-static.md
diff --git a/src/fight-with-compiler/lifetime/intro.md b/src/practice/fight-with-compiler/lifetime/intro.md
similarity index 100%
rename from src/fight-with-compiler/lifetime/intro.md
rename to src/practice/fight-with-compiler/lifetime/intro.md
diff --git a/src/fight-with-compiler/lifetime/loop.md b/src/practice/fight-with-compiler/lifetime/loop.md
similarity index 100%
rename from src/fight-with-compiler/lifetime/loop.md
rename to src/practice/fight-with-compiler/lifetime/loop.md
diff --git a/src/fight-with-compiler/lifetime/too-long1.md b/src/practice/fight-with-compiler/lifetime/too-long1.md
similarity index 100%
rename from src/fight-with-compiler/lifetime/too-long1.md
rename to src/practice/fight-with-compiler/lifetime/too-long1.md
diff --git a/src/fight-with-compiler/lifetime/too-long2.md b/src/practice/fight-with-compiler/lifetime/too-long2.md
similarity index 100%
rename from src/fight-with-compiler/lifetime/too-long2.md
rename to src/practice/fight-with-compiler/lifetime/too-long2.md
diff --git a/src/fight-with-compiler/phantom-data.md b/src/practice/fight-with-compiler/phantom-data.md
similarity index 100%
rename from src/fight-with-compiler/phantom-data.md
rename to src/practice/fight-with-compiler/phantom-data.md
diff --git a/src/fight-with-compiler/unconstrained.md b/src/practice/fight-with-compiler/unconstrained.md
similarity index 100%
rename from src/fight-with-compiler/unconstrained.md
rename to src/practice/fight-with-compiler/unconstrained.md
diff --git a/src/practice/intro.md b/src/practice/intro.md
index 7f89d0ee..805221d7 100644
--- a/src/practice/intro.md
+++ b/src/practice/intro.md
@@ -1,2 +1,3 @@
# Rust最佳实践
+
对于生产级项目而言,运行稳定性和可维护性是非常重要的,本章就一起来看看 Rust 项目有哪些最佳实践准则。
\ No newline at end of file
diff --git a/src/pitfalls/arithmetic-overflow.md b/src/practice/pitfalls/arithmetic-overflow.md
similarity index 100%
rename from src/pitfalls/arithmetic-overflow.md
rename to src/practice/pitfalls/arithmetic-overflow.md
diff --git a/src/pitfalls/closure-with-lifetime.md b/src/practice/pitfalls/closure-with-lifetime.md
similarity index 100%
rename from src/pitfalls/closure-with-lifetime.md
rename to src/practice/pitfalls/closure-with-lifetime.md
diff --git a/src/pitfalls/index.md b/src/practice/pitfalls/index.md
similarity index 100%
rename from src/pitfalls/index.md
rename to src/practice/pitfalls/index.md
diff --git a/src/pitfalls/iterator-everywhere.md b/src/practice/pitfalls/iterator-everywhere.md
similarity index 100%
rename from src/pitfalls/iterator-everywhere.md
rename to src/practice/pitfalls/iterator-everywhere.md
diff --git a/src/pitfalls/lazy-iterators.md b/src/practice/pitfalls/lazy-iterators.md
similarity index 100%
rename from src/pitfalls/lazy-iterators.md
rename to src/practice/pitfalls/lazy-iterators.md
diff --git a/src/pitfalls/main-with-channel-blocked.md b/src/practice/pitfalls/main-with-channel-blocked.md
similarity index 100%
rename from src/pitfalls/main-with-channel-blocked.md
rename to src/practice/pitfalls/main-with-channel-blocked.md
diff --git a/src/pitfalls/multiple-mutable-references.md b/src/practice/pitfalls/multiple-mutable-references.md
similarity index 100%
rename from src/pitfalls/multiple-mutable-references.md
rename to src/practice/pitfalls/multiple-mutable-references.md
diff --git a/src/pitfalls/stack-overflow.md b/src/practice/pitfalls/stack-overflow.md
similarity index 100%
rename from src/pitfalls/stack-overflow.md
rename to src/practice/pitfalls/stack-overflow.md
diff --git a/src/pitfalls/the-disabled-mutability.md b/src/practice/pitfalls/the-disabled-mutability.md
similarity index 100%
rename from src/pitfalls/the-disabled-mutability.md
rename to src/practice/pitfalls/the-disabled-mutability.md
diff --git a/src/pitfalls/use-vec-in-for.md b/src/practice/pitfalls/use-vec-in-for.md
similarity index 100%
rename from src/pitfalls/use-vec-in-for.md
rename to src/practice/pitfalls/use-vec-in-for.md
diff --git a/src/pitfalls/utf8-performance.md b/src/practice/pitfalls/utf8-performance.md
similarity index 100%
rename from src/pitfalls/utf8-performance.md
rename to src/practice/pitfalls/utf8-performance.md
diff --git a/src/pitfalls/weird-ranges.md b/src/practice/pitfalls/weird-ranges.md
similarity index 100%
rename from src/pitfalls/weird-ranges.md
rename to src/practice/pitfalls/weird-ranges.md
diff --git a/src/test/unit-integration-test.md b/src/test/unit-integration-test.md
index 3d7e6636..a639d251 100644
--- a/src/test/unit-integration-test.md
+++ b/src/test/unit-integration-test.md
@@ -37,7 +37,7 @@ mod tests {
在 `#[cfg(test)]` 中,`cfg` 是配置 `configuration` 的缩写,它告诉 Rust :当 `test` 配置项存在时,才运行下面的代码,而 `cargo test` 在运行时,就会将 `test` 这个配置项传入进来,因此后面的 `tests` 模块会被包含进来。
-大家看出来了吗?这是典型的条件编译,`Cargo` 会根据指定的配置来选择是否编译指定的代码,事实上关于条件编译 Rust 能做的不仅仅是这些,在 [`Cargo` 专题](https://course.rs/cargo/intro.html)中我们会进行更为详细的介绍。
+大家看出来了吗?这是典型的条件编译,`Cargo` 会根据指定的配置来选择是否编译指定的代码,事实上关于条件编译 Rust 能做的不仅仅是这些,在 [`Cargo` 专题](https://course.rs/toolchains/cargo/intro.html)中我们会进行更为详细的介绍。
#### 测试私有函数
diff --git a/src/too-many-lists/deque/final-code.md b/src/too-many-lists/deque/final-code.md
new file mode 100644
index 00000000..3547589d
--- /dev/null
+++ b/src/too-many-lists/deque/final-code.md
@@ -0,0 +1,247 @@
+# 最终代码
+这一章真不好写( 也很难翻译... ),最终我们实现了一个 100% 安全但是功能残缺的双向链表。
+
+同时在实现中,还有大量 `Rc` 和 `RefCell` 引起的运行时检查,最终会影响链表的性能。整个双向链表实现史就是一部别名和所有权的奋斗史。
+
+总之,不管爱与不爱,它就这样了,特别是如果我们不在意内部的细节暴露给外面用户时。
+
+而从下一章开始,我们将实现一个真正能够全盘掌控的链表,当然...通过 unsafe 代码实现!
+
+
+```rust
+
+#![allow(unused)]
+fn main() {
+use std::rc::Rc;
+use std::cell::{Ref, RefMut, RefCell};
+
+pub struct List {
+ head: Link,
+ tail: Link,
+}
+
+type Link = Option>>>;
+
+struct Node {
+ elem: T,
+ next: Link,
+ prev: Link,
+}
+
+
+impl Node {
+ fn new(elem: T) -> Rc> {
+ Rc::new(RefCell::new(Node {
+ elem: elem,
+ prev: None,
+ next: None,
+ }))
+ }
+}
+
+impl List {
+ pub fn new() -> Self {
+ List { head: None, tail: None }
+ }
+
+ pub fn push_front(&mut self, elem: T) {
+ let new_head = Node::new(elem);
+ match self.head.take() {
+ Some(old_head) => {
+ old_head.borrow_mut().prev = Some(new_head.clone());
+ new_head.borrow_mut().next = Some(old_head);
+ self.head = Some(new_head);
+ }
+ None => {
+ self.tail = Some(new_head.clone());
+ self.head = Some(new_head);
+ }
+ }
+ }
+
+ pub fn push_back(&mut self, elem: T) {
+ let new_tail = Node::new(elem);
+ match self.tail.take() {
+ Some(old_tail) => {
+ old_tail.borrow_mut().next = Some(new_tail.clone());
+ new_tail.borrow_mut().prev = Some(old_tail);
+ self.tail = Some(new_tail);
+ }
+ None => {
+ self.head = Some(new_tail.clone());
+ self.tail = Some(new_tail);
+ }
+ }
+ }
+
+ pub fn pop_back(&mut self) -> Option {
+ self.tail.take().map(|old_tail| {
+ match old_tail.borrow_mut().prev.take() {
+ Some(new_tail) => {
+ new_tail.borrow_mut().next.take();
+ self.tail = Some(new_tail);
+ }
+ None => {
+ self.head.take();
+ }
+ }
+ Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem
+ })
+ }
+
+ pub fn pop_front(&mut self) -> Option {
+ self.head.take().map(|old_head| {
+ match old_head.borrow_mut().next.take() {
+ Some(new_head) => {
+ new_head.borrow_mut().prev.take();
+ self.head = Some(new_head);
+ }
+ None => {
+ self.tail.take();
+ }
+ }
+ Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem
+ })
+ }
+
+ pub fn peek_front(&self) -> Option[> {
+ self.head.as_ref().map(|node| {
+ Ref::map(node.borrow(), |node| &node.elem)
+ })
+ }
+
+ pub fn peek_back(&self) -> Option][> {
+ self.tail.as_ref().map(|node| {
+ Ref::map(node.borrow(), |node| &node.elem)
+ })
+ }
+
+ pub fn peek_back_mut(&mut self) -> Option> {
+ self.tail.as_ref().map(|node| {
+ RefMut::map(node.borrow_mut(), |node| &mut node.elem)
+ })
+ }
+
+ pub fn peek_front_mut(&mut self) -> Option> {
+ self.head.as_ref().map(|node| {
+ RefMut::map(node.borrow_mut(), |node| &mut node.elem)
+ })
+ }
+
+ pub fn into_iter(self) -> IntoIter {
+ IntoIter(self)
+ }
+}
+
+impl Drop for List {
+ fn drop(&mut self) {
+ while self.pop_front().is_some() {}
+ }
+}
+
+pub struct IntoIter(List);
+
+impl Iterator for IntoIter {
+ type Item = T;
+
+ fn next(&mut self) -> Option {
+ self.0.pop_front()
+ }
+}
+
+impl DoubleEndedIterator for IntoIter {
+ fn next_back(&mut self) -> Option {
+ self.0.pop_back()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::List;
+
+ #[test]
+ fn basics() {
+ let mut list = List::new();
+
+ // Check empty list behaves right
+ assert_eq!(list.pop_front(), None);
+
+ // Populate list
+ list.push_front(1);
+ list.push_front(2);
+ list.push_front(3);
+
+ // Check normal removal
+ assert_eq!(list.pop_front(), Some(3));
+ assert_eq!(list.pop_front(), Some(2));
+
+ // Push some more just to make sure nothing's corrupted
+ list.push_front(4);
+ list.push_front(5);
+
+ // Check normal removal
+ assert_eq!(list.pop_front(), Some(5));
+ assert_eq!(list.pop_front(), Some(4));
+
+ // Check exhaustion
+ assert_eq!(list.pop_front(), Some(1));
+ assert_eq!(list.pop_front(), None);
+
+ // ---- back -----
+
+ // Check empty list behaves right
+ assert_eq!(list.pop_back(), None);
+
+ // Populate list
+ list.push_back(1);
+ list.push_back(2);
+ list.push_back(3);
+
+ // Check normal removal
+ assert_eq!(list.pop_back(), Some(3));
+ assert_eq!(list.pop_back(), Some(2));
+
+ // Push some more just to make sure nothing's corrupted
+ list.push_back(4);
+ list.push_back(5);
+
+ // Check normal removal
+ assert_eq!(list.pop_back(), Some(5));
+ assert_eq!(list.pop_back(), Some(4));
+
+ // Check exhaustion
+ assert_eq!(list.pop_back(), Some(1));
+ assert_eq!(list.pop_back(), None);
+ }
+
+ #[test]
+ fn peek() {
+ let mut list = List::new();
+ assert!(list.peek_front().is_none());
+ assert!(list.peek_back().is_none());
+ assert!(list.peek_front_mut().is_none());
+ assert!(list.peek_back_mut().is_none());
+
+ list.push_front(1); list.push_front(2); list.push_front(3);
+
+ assert_eq!(&*list.peek_front().unwrap(), &3);
+ assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3);
+ assert_eq!(&*list.peek_back().unwrap(), &1);
+ assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1);
+ }
+
+ #[test]
+ fn into_iter() {
+ let mut list = List::new();
+ list.push_front(1); list.push_front(2); list.push_front(3);
+
+ let mut iter = list.into_iter();
+ assert_eq!(iter.next(), Some(3));
+ assert_eq!(iter.next_back(), Some(1));
+ assert_eq!(iter.next(), Some(2));
+ assert_eq!(iter.next_back(), None);
+ assert_eq!(iter.next(), None);
+ }
+}
+}
+```
\ No newline at end of file
diff --git a/src/too-many-lists/deque/iterator.md b/src/too-many-lists/deque/iterator.md
new file mode 100644
index 00000000..23be7d6f
--- /dev/null
+++ b/src/too-many-lists/deque/iterator.md
@@ -0,0 +1,238 @@
+# 迭代器
+坏男孩最令人头疼,而链表实现中,迭代器就是这样的坏男孩,所以我们放在最后来处理。
+
+## IntoIter
+由于是转移所有权,因此 `IntoIter` 一直都是最好实现的:
+```rust
+pub struct IntoIter(List);
+
+impl List {
+ pub fn into_iter(self) -> IntoIter {
+ IntoIter(self)
+ }
+}
+
+impl Iterator for IntoIter {
+ type Item = T;
+ fn next(&mut self) -> Option {
+ self.0.pop_front()
+ }
+}
+```
+
+但是关于双向链表,有一个有趣的事实,它不仅可以从前向后迭代,还能反过来。前面实现的是传统的从前到后,那问题来了,反过来该如何实现呢?
+
+答案是: `DoubleEndedIterator`,它继承自 `Iterator`( 通过 [`supertrait`](https://course.rs/basic/trait/advance-trait.html?highlight=supertrait#特征定义中的特征约束) ),因此意味着要实现该特征,首先需要实现 `Iterator`。
+
+这样只要为 `DoubleEndedIterator` 实现 `next_back` 方法,就可以支持双向迭代了: `Iterator` 的 `next` 方法从前往后,而 `next_back` 从后向前。
+
+```rust
+impl DoubleEndedIterator for IntoIter {
+ fn next_back(&mut self) -> Option {
+ self.0.pop_back()
+ }
+}
+```
+
+测试下:
+```rust
+#[test]
+fn into_iter() {
+ let mut list = List::new();
+ list.push_front(1); list.push_front(2); list.push_front(3);
+
+ let mut iter = list.into_iter();
+ assert_eq!(iter.next(), Some(3));
+ assert_eq!(iter.next_back(), Some(1));
+ assert_eq!(iter.next(), Some(2));
+ assert_eq!(iter.next_back(), None);
+ assert_eq!(iter.next(), None);
+}
+```
+
+```shell
+cargo test
+
+ Running target/debug/lists-5c71138492ad4b4a
+
+running 11 tests
+test fourth::test::basics ... ok
+test fourth::test::peek ... ok
+test fourth::test::into_iter ... ok
+test first::test::basics ... ok
+test second::test::basics ... ok
+test second::test::iter ... ok
+test second::test::iter_mut ... ok
+test third::test::iter ... ok
+test third::test::basics ... ok
+test second::test::into_iter ... ok
+test second::test::peek ... ok
+
+test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured
+```
+
+## Iter
+这里又要用到糟糕的 `Ref`:
+```rust
+pub struct Iter<'a, T>(Option][>>);
+
+impl List {
+ pub fn iter(&self) -> Iter {
+ Iter(self.head.as_ref().map(|head| head.borrow()))
+ }
+}
+```
+
+```shell
+> cargo build
+```
+
+迄今为止一切运行正常,接下来的 `next` 实现起来会有些麻烦:
+```rust
+impl<'a, T> Iterator for Iter<'a, T> {
+ type Item = Ref<'a, T>;
+ fn next(&mut self) -> Option {
+ self.0.take().map(|node_ref| {
+ self.0 = node_ref.next.as_ref().map(|head| head.borrow());
+ Ref::map(node_ref, |node| &node.elem)
+ })
+ }
+}
+```
+
+```shell
+cargo build
+
+error[E0521]: borrowed data escapes outside of closure
+ --> src/fourth.rs:155:13
+ |
+153 | fn next(&mut self) -> Option {
+ | --------- `self` is declared here, outside of the closure body
+154 | self.0.take().map(|node_ref| {
+155 | self.0 = node_ref.next.as_ref().map(|head| head.borrow());
+ | ^^^^^^ -------- borrow is only valid in the closure body
+ | |
+ | reference to `node_ref` escapes the closure body here
+
+error[E0505]: cannot move out of `node_ref` because it is borrowed
+ --> src/fourth.rs:156:22
+ |
+153 | fn next(&mut self) -> Option {
+ | --------- lifetime `'1` appears in the type of `self`
+154 | self.0.take().map(|node_ref| {
+155 | self.0 = node_ref.next.as_ref().map(|head| head.borrow());
+ | ------ -------- borrow of `node_ref` occurs here
+ | |
+ | assignment requires that `node_ref` is borrowed for `'1`
+156 | Ref::map(node_ref, |node| &node.elem)
+ | ^^^^^^^^ move out of `node_ref` occurs here
+```
+
+果然,膝盖又中了一箭。
+
+`node_ref` 活得不够久,跟一般的引用不同,Rust 不允许我们这样分割 `Ref`,从 `head.borrow()` 中取出的 `Ref` 只允许跟 `node_ref` 活得一样久。
+
+
+而我们想要的函数是存在的:
+```rust
+pub fn map_split(orig: Ref<'b, T>, f: F) -> (Ref<'b, U>, Ref<'b, V>) where
+ F: FnOnce(&T) -> (&U, &V),
+ U: ?Sized,
+ V: ?Sized,
+```
+
+喔,这个函数定义的泛型直接晃瞎了我的眼睛。。
+```rust
+fn next(&mut self) -> Option {
+ self.0.take().map(|node_ref| {
+ let (next, elem) = Ref::map_split(node_ref, |node| {
+ (&node.next, &node.elem)
+ });
+
+ self.0 = next.as_ref().map(|head| head.borrow());
+
+ elem
+ })
+}
+```
+
+```shell
+cargo build
+ Compiling lists v0.1.0 (/Users/ABeingessner/dev/temp/lists)
+error[E0521]: borrowed data escapes outside of closure
+ --> src/fourth.rs:159:13
+ |
+153 | fn next(&mut self) -> Option {
+ | --------- `self` is declared here, outside of the closure body
+...
+159 | self.0 = next.as_ref().map(|head| head.borrow());
+ | ^^^^^^ ---- borrow is only valid in the closure body
+ | |
+ | reference to `next` escapes the closure body here
+```
+
+额,借用的内容只允许在闭包体中使用,看起来我们还是得用 `Ref::map` 来解决问题:
+```rust
+fn next(&mut self) -> Option {
+ self.0.take().map(|node_ref| {
+ let (next, elem) = Ref::map_split(node_ref, |node| {
+ (&node.next, &node.elem)
+ });
+
+ self.0 = if next.is_some() {
+ Some(Ref::map(next, |next| &**next.as_ref().unwrap()))
+ } else {
+ None
+ };
+
+ elem
+ })
+}
+```
+
+```shell
+error[E0308]: mismatched types
+ --> src/fourth.rs:162:22
+ |
+162 | Some(Ref::map(next, |next| &**next.as_ref().unwrap()))
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `fourth::Node`, found struct `std::cell::RefCell`
+ |
+ = note: expected type `std::cell::Ref<'_, fourth::Node<_>>`
+ found type `std::cell::Ref<'_, std::cell::RefCell>>`
+```
+
+晕, 多了一个 `RefCell` ,随着我们的对链表的逐步深入,`RefCell` 的代码嵌套变成了不可忽视的问题。
+
+看起来我们已经无能为力了,只能试着去摆脱 `RefCell` 了。`Rc` 怎么样?我们完全可以对 `Rc` 进行完整的克隆:
+```rust
+pub struct Iter(Option>>);
+
+impl List {
+ pub fn iter(&self) -> Iter {
+ Iter(self.head.as_ref().map(|head| head.clone()))
+ }
+}
+
+impl Iterator for Iter {
+ type Item =
+```
+
+等等,那现在返回的是什么?`&T` 还是 `Ref` ?
+
+两者都不是,现在我们的 `Iter` 已经没有生命周期了:无论是 `&T` 还是 `Ref` 都需要我们在 `next` 之前声明好生命周期。但是我们试图从 `Rc` 中取出来的值其实是迭代器的引用。
+
+也可以通过对 `Rc` 进行 map 获取到 `Rc`?但是标准库并没有给我们提供相应的功能,第三方倒是有[一个](https://crates.io/crates/owning_ref)。
+
+但是,即使这么做了,还有一个更大的坑在等着:一个会造成迭代器不合法的可怕幽灵。事实上,之前我们对于迭代器不合法是免疫的,但是一旦迭代器产生 `Rc`,那它们就不再会借用链表。这意味着人们可以在持有指向链表内部的指针时,还可以进行 `push` 和 `pop` 操作。
+
+严格来说,`push` 问题不大,因为链表两端的增长不会对我们正在关注的某个子链表造成影响。
+
+但是 `pop` 就是另一个故事了,如果在我们关注的子链表之外 `pop`, 那问题不大。但是如果是 `pop` 一个正在引用的子链表中的节点呢?那一切就完了,特别是,如果大家还试图去 unwrap `try_unwrap` 返回的 `Result` ,会直接造成整个程序的 `panic`。
+
+仔细想一想,好像也不错,程序一切正常,除非去 `pop` 我们正在引用的节点,最美的是,就算遇到这种情况,程序也会直接崩溃,提示我们错误的发生。
+
+其实我们大部分的努力都是为了实现隐藏的细节和优雅的 API,典型的二八原则,八成时间花在二成的细节上。但是如果不关心这些细节,可以接受自己的平凡的话,那把节点简单的到处传递就行。
+
+总之,可以看出,内部可变性非常适合写一个安全性的应用程序,但是如果是安全性高的库,那内部可变性就有些捉襟见肘了。
+
+最终,我选择了放弃,不再实现 `Iter` 和 `IterMut`,也许努力下,可以实现,但是。。。不愉快,算了。
\ No newline at end of file
diff --git a/src/too-many-lists/deque/peek.md b/src/too-many-lists/deque/peek.md
new file mode 100644
index 00000000..f9fda431
--- /dev/null
+++ b/src/too-many-lists/deque/peek.md
@@ -0,0 +1,136 @@
+# Peek
+`push` 和 `pop` 的防不胜防的编译报错着实让人出了些冷汗,下面来看看轻松的,至少在之前的链表中是很轻松的 :)
+
+```rust
+pub fn peek_front(&self) -> Option<&T> {
+ self.head.as_ref().map(|node| {
+ &node.elem
+ })
+}
+```
+
+额...好像被人发现我是复制黏贴的了,赶紧换一个:
+```rust
+pub fn peek_front(&self) -> Option<&T> {
+ self.head.as_ref().map(|node| {
+ // BORROW!!!!
+ &node.borrow().elem
+ })
+}
+```
+
+```shell
+cargo build
+
+error[E0515]: cannot return value referencing temporary value
+ --> src/fourth.rs:66:13
+ |
+66 | &node.borrow().elem
+ | ^ ----------^^^^^
+ | | |
+ | | temporary value created here
+ | |
+ | returns a value referencing data owned by the current function
+```
+
+从报错可以看出,原因是我们引用了局部的变量并试图在函数中返回。为了解释这个问题,先来看看 `borrow` 的定义:
+```rust
+fn borrow<'a>(&'a self) -> Ref<'a, T>
+fn borrow_mut<'a>(&'a self) -> RefMut<'a, T>
+```
+
+这里返回的并不是 `&T` 或 `&mut T`,而是一个 [`Ref`](https://doc.rust-lang.org/std/cell/struct.Ref.html) 和 [`RefMut`](https://doc.rust-lang.org/std/cell/struct.RefMut.html),那么它们是什么?说白了,它们就是在借用到的引用外包裹了一层。而且 `Ref` 和 `RefMut` 分别实现了 `Deref` 和 `DerefMut`,在绝大多数场景中,我们都可以像使用 `&T` 一样去使用它们。
+
+
+只能说是成是败都赖萧何,恰恰就因为这一层包裹,导致生命周期改变了,也就是 `Ref` 和内部引用的生命周期不再和 `RefCell` 相同,而 `Ref` 的生命周期是什么,相信大家都能看得出来,因此就造成了局部引用的问题。
+
+事实上,这是必须的,如果内部的引用和外部的 `Ref` 生命周期不一致,那该如何管理?当 `Ref` 因超出作用域被 `drop` 时,内部的引用怎么办?
+
+现在该怎么办?我们只想要一个引用,现在却多了一个 `Ref` 拦路虎。等等,如果我们不返回 `&T` 而是返回 `Ref` 呢?
+```rust
+use std::cell::{Ref, RefCell};
+
+pub fn peek_front(&self) -> Option][> {
+ self.head.as_ref().map(|node| {
+ node.borrow()
+ })
+}
+```
+
+```shell
+> cargo build
+
+error[E0308]: mismatched types
+ --> src/fourth.rs:64:9
+ |
+64 | / self.head.as_ref().map(|node| {
+65 | | node.borrow()
+66 | | })
+ | |__________^ expected type parameter, found struct `fourth::Node`
+ |
+ = note: expected type `std::option::Option>`
+ found type `std::option::Option>>`
+```
+
+嗯,类型不匹配了,要返回的是 `Ref` 但是获取的却是 `Ref>`,那么现在看上去有两个选择:
+
+- 抛弃这条路,换一条重新开始
+- 一条路走到死,最终通过更复杂的实现来解决
+
+但是,仔细想想,这两个选择都不是我们想要的,那没办法了,只能继续深挖,看看有没有其它解决办法。啊哦,还真发现了一只野兽:
+```rust
+map(orig: Ref<'b, T>, f: F) -> Ref<'b, U>
+ where F: FnOnce(&T) -> &U,
+ U: ?Sized
+```
+
+就像在 `Result` 和 `Option` 上使用 `map` 一样,我们还能在 `Ref` 上使用 `map`:
+```rust
+pub fn peek_front(&self) -> Option][> {
+ self.head.as_ref().map(|node| {
+ Ref::map(node.borrow(), |node| &node.elem)
+ })
+}
+```
+
+```shell
+> cargo build
+```
+
+Gooood! 本章节的编译错误可以说是多个链表中最难解决的之一,依然被我们成功搞定了!
+
+
+下面来写下测试用例,需要注意的是 `Ref` 不能被直接比较,因此我们需要先利用 `Deref` 解引用出其中的值,再进行比较。
+
+```rust
+#[test]
+fn peek() {
+ let mut list = List::new();
+ assert!(list.peek_front().is_none());
+ list.push_front(1); list.push_front(2); list.push_front(3);
+
+ assert_eq!(&*list.peek_front().unwrap(), &3);
+}
+```
+
+```shell
+> cargo test
+
+ Running target/debug/lists-5c71138492ad4b4a
+
+running 10 tests
+test first::test::basics ... ok
+test fourth::test::basics ... ok
+test second::test::basics ... ok
+test fourth::test::peek ... ok
+test second::test::iter_mut ... ok
+test second::test::into_iter ... ok
+test third::test::basics ... ok
+test second::test::peek ... ok
+test second::test::iter ... ok
+test third::test::iter ... ok
+
+test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured
+```
+
+终于可以把文章开头的冷汗擦拭干净了,忘掉这个章节吧,让我来养你...哦不对,让我们开始一段真正轻松的章节。
\ No newline at end of file
diff --git a/src/too-many-lists/deque/symmetric.md b/src/too-many-lists/deque/symmetric.md
new file mode 100644
index 00000000..c97584c7
--- /dev/null
+++ b/src/too-many-lists/deque/symmetric.md
@@ -0,0 +1,161 @@
+# 基本操作的对称镜像
+之前我们仅实现了头部的 `push`、`pop` ,现在来补全一下,大自然的对称之美咱的双向链表也不能少了。
+
+```rust
+tail <-> head
+next <-> prev
+front -> back
+```
+
+需要注意的是,这里还新增了 `mut` 类型的 peek:
+```rust
+use std::cell::{Ref, RefCell, RefMut};
+
+//..
+
+pub fn push_back(&mut self, elem: T) {
+ let new_tail = Node::new(elem);
+ match self.tail.take() {
+ Some(old_tail) => {
+ old_tail.borrow_mut().next = Some(new_tail.clone());
+ new_tail.borrow_mut().prev = Some(old_tail);
+ self.tail = Some(new_tail);
+ }
+ None => {
+ self.head = Some(new_tail.clone());
+ self.tail = Some(new_tail);
+ }
+ }
+}
+
+pub fn pop_back(&mut self) -> Option {
+ self.tail.take().map(|old_tail| {
+ match old_tail.borrow_mut().prev.take() {
+ Some(new_tail) => {
+ new_tail.borrow_mut().next.take();
+ self.tail = Some(new_tail);
+ }
+ None => {
+ self.head.take();
+ }
+ }
+ Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem
+ })
+}
+
+pub fn peek_back(&self) -> Option][> {
+ self.tail.as_ref().map(|node| {
+ Ref::map(node.borrow(), |node| &node.elem)
+ })
+}
+
+pub fn peek_back_mut(&mut self) -> Option> {
+ self.tail.as_ref().map(|node| {
+ RefMut::map(node.borrow_mut(), |node| &mut node.elem)
+ })
+}
+
+pub fn peek_front_mut(&mut self) -> Option> {
+ self.head.as_ref().map(|node| {
+ RefMut::map(node.borrow_mut(), |node| &mut node.elem)
+ })
+}
+```
+
+再更新测试用例:
+```rust
+#[test]
+fn basics() {
+ let mut list = List::new();
+
+ // Check empty list behaves right
+ assert_eq!(list.pop_front(), None);
+
+ // Populate list
+ list.push_front(1);
+ list.push_front(2);
+ list.push_front(3);
+
+ // Check normal removal
+ assert_eq!(list.pop_front(), Some(3));
+ assert_eq!(list.pop_front(), Some(2));
+
+ // Push some more just to make sure nothing's corrupted
+ list.push_front(4);
+ list.push_front(5);
+
+ // Check normal removal
+ assert_eq!(list.pop_front(), Some(5));
+ assert_eq!(list.pop_front(), Some(4));
+
+ // Check exhaustion
+ assert_eq!(list.pop_front(), Some(1));
+ assert_eq!(list.pop_front(), None);
+
+ // ---- back -----
+
+ // Check empty list behaves right
+ assert_eq!(list.pop_back(), None);
+
+ // Populate list
+ list.push_back(1);
+ list.push_back(2);
+ list.push_back(3);
+
+ // Check normal removal
+ assert_eq!(list.pop_back(), Some(3));
+ assert_eq!(list.pop_back(), Some(2));
+
+ // Push some more just to make sure nothing's corrupted
+ list.push_back(4);
+ list.push_back(5);
+
+ // Check normal removal
+ assert_eq!(list.pop_back(), Some(5));
+ assert_eq!(list.pop_back(), Some(4));
+
+ // Check exhaustion
+ assert_eq!(list.pop_back(), Some(1));
+ assert_eq!(list.pop_back(), None);
+}
+
+#[test]
+fn peek() {
+ let mut list = List::new();
+ assert!(list.peek_front().is_none());
+ assert!(list.peek_back().is_none());
+ assert!(list.peek_front_mut().is_none());
+ assert!(list.peek_back_mut().is_none());
+
+ list.push_front(1); list.push_front(2); list.push_front(3);
+
+ assert_eq!(&*list.peek_front().unwrap(), &3);
+ assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3);
+ assert_eq!(&*list.peek_back().unwrap(), &1);
+ assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1);
+}
+```
+
+什么?你问我这里的测试用例全吗?只能说如果测试全部的组合情况,这一章节会被撑爆。至于现在,能不出错就谢天谢地了 :(
+
+```shell
+> cargo test
+
+ Running target/debug/lists-5c71138492ad4b4a
+
+running 10 tests
+test first::test::basics ... ok
+test fourth::test::basics ... ok
+test second::test::basics ... ok
+test fourth::test::peek ... ok
+test second::test::iter ... ok
+test third::test::iter ... ok
+test second::test::into_iter ... ok
+test second::test::iter_mut ... ok
+test second::test::peek ... ok
+test third::test::basics ... ok
+
+test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured
+```
+
+我想说:Ctrl CV 是最好的编程工具,大家同意吗?
\ No newline at end of file
diff --git a/src/too-many-lists/unsafe-queue/basics.md b/src/too-many-lists/unsafe-queue/basics.md
new file mode 100644
index 00000000..3a3c17a4
--- /dev/null
+++ b/src/too-many-lists/unsafe-queue/basics.md
@@ -0,0 +1,246 @@
+# 基本操作
+
+> 本章节的代码中有一个隐藏的 bug,因为它藏身于 unsafe 中,因此不会导致报错,我们会在后续章节解决这个问题,所以,请不要在生产环境使用此处的代码
+
+在开始之前,大家需要先了解 unsafe 的[相关知识](https://course.rs/advance/unsafe/intro.html)。那么,言归正传,该如何构建一个链表?在之前我们是这么做的:
+```rust
+impl List {
+ pub fn new() -> Self {
+ List { head: None, tail: None }
+ }
+}
+```
+
+但是我们不再在 `tail` 中使用 `Option`:
+```shell
+> cargo build
+
+error[E0308]: mismatched types
+ --> src/fifth.rs:15:34
+ |
+15 | List { head: None, tail: None }
+ | ^^^^ expected *-ptr, found
+ | enum `std::option::Option`
+ |
+ = note: expected type `*mut fifth::Node`
+ found type `std::option::Option<_>`
+```
+
+我们是可以使用 `Option` 包裹一层,但是 `*mut` 裸指针之所以裸,是因为它狂,它可以是 `null` ! 因此 `Option` 就变得没有意义:
+```rust
+use std::ptr;
+
+// defns...
+
+impl List {
+ pub fn new() -> Self {
+ List { head: None, tail: ptr::null_mut() }
+ }
+}
+```
+
+如上所示,通过 `std::ptr::null_mut` 函数可以获取一个 `null`,当然,还可以使用 `0 as *mut _`,但是...已经这么不安全了,好歹我们要留一点代码可读性上的尊严吧 = , =
+
+好了,现在是时候去重新实现 `push` ,之前获取的是 `Option<&mut Node>` 成为我们的拦路虎,这次来看看如果是获取 `*mut Node` 还会不会有类似的问题。
+
+首先,该如何将一个普通的引用变成裸指针?答案是:强制转换 Coercions。
+
+```rust
+let raw_tail: *mut _ = &mut *new_tail;
+```
+
+来看看 `push` 的实现:
+```rust
+pub fn push(&mut self, elem: T) {
+ let mut new_tail = Box::new(Node {
+ elem: elem,
+ next: None,
+ });
+
+ let raw_tail: *mut _ = &mut *new_tail;
+
+ // .is_null 会检查是否为 null, 在功能上等价于 `None` 的检查
+ if !self.tail.is_null() {
+ // 如果 old tail 存在,那将其指向新的 tail
+ self.tail.next = Some(new_tail);
+ } else {
+ // 否则让 head 指向新的 tail
+ self.head = Some(new_tail);
+ }
+
+ self.tail = raw_tail;
+}
+```
+
+```shell
+> cargo build
+
+error[E0609]: no field `next` on type `*mut fifth::Node`
+ --> src/fifth.rs:31:23
+ |
+31 | self.tail.next = Some(new_tail);
+ | ----------^^^^
+ | |
+ | help: `self.tail` is a raw pointer;
+ | try dereferencing it: `(*self.tail).next`
+```
+
+当使用裸指针时,一些 Rust 提供的便利条件也将不复存在,例如由于不安全性的存在,裸指针需要我们手动去解引用( deref ):
+```rust
+*self.tail.next = Some(new_tail);
+```
+
+```shell
+> cargo build
+
+error[E0609]: no field `next` on type `*mut fifth::Node`
+ --> src/fifth.rs:31:23
+ |
+31 | *self.tail.next = Some(new_tail);
+ | -----------^^^^
+ | |
+ | help: `self.tail` is a raw pointer;
+ | try dereferencing it: `(*self.tail).next`
+```
+
+哦哦,运算符的优先级问题:
+```rust
+(*self.tail).next = Some(new_tail);
+```
+
+```shell
+> cargo build
+
+error[E0133]: dereference of raw pointer is unsafe and requires
+ unsafe function or block
+
+ --> src/fifth.rs:31:13
+ |
+31 | (*self.tail).next = Some(new_tail);
+ | ^^^^^^^^^^^^^^^^^ dereference of raw pointer
+ |
+ = note: raw pointers may be NULL, dangling or unaligned;
+ they can violate aliasing rules and cause data races:
+ all of these are undefined behavior
+```
+
+哎...太难了,错误一个连一个,好在编译器给出了提示:由于我们在进行不安全的操作,因此需要使用 `unsafe` 语句块。那么问题来了,是将某几行代码包在 `unsafe` 中还是将整个函数包在 `unsafe` 中呢?如果大家不知道哪个是正确答案的话,证明[之前的章节](https://course.rs/advance/unsafe/intro.html#控制-unsafe-的使用边界)还是没有仔细学,请回去再看一下,巩固巩固:)
+
+```rust
+pub fn push(&mut self, elem: T) {
+ let mut new_tail = Box::new(Node {
+ elem: elem,
+ next: None,
+ });
+
+ let raw_tail: *mut _ = &mut *new_tail;
+
+ if !self.tail.is_null() {
+ // 你好编译器,我知道我在做危险的事情,我向你保证:就算犯错了,也和你没有关系,都是我这个不优秀的程序员的责任
+ unsafe {
+ (*self.tail).next = Some(new_tail);
+ }
+ } else {
+ self.head = Some(new_tail);
+ }
+
+ self.tail = raw_tail;
+}
+```
+
+```shell
+> cargo build
+warning: field is never used: `elem`
+ --> src/fifth.rs:11:5
+ |
+11 | elem: T,
+ | ^^^^^^^
+ |
+ = note: #[warn(dead_code)] on by default
+```
+
+细心的同学可能会发现:不是所有的裸指针代码都有 unsafe 的身影。原因在于:**创建原生指针是安全的行为,而解引用原生指针才是不安全的行为**
+
+呼,长出了一口气,终于成功实现了 `push` ,下面来看看 `pop`:
+```rust
+pub fn pop(&mut self) -> Option {
+ self.head.take().map(|head| {
+ let head = *head;
+ self.head = head.next;
+
+ if self.head.is_none() {
+ self.tail = ptr::null_mut();
+ }
+
+ head.elem
+ })
+}
+```
+
+测试下:
+```rust
+#[cfg(test)]
+mod test {
+ use super::List;
+ #[test]
+ fn basics() {
+ let mut list = List::new();
+
+ // Check empty list behaves right
+ assert_eq!(list.pop(), None);
+
+ // Populate list
+ list.push(1);
+ list.push(2);
+ list.push(3);
+
+ // Check normal removal
+ assert_eq!(list.pop(), Some(1));
+ assert_eq!(list.pop(), Some(2));
+
+ // Push some more just to make sure nothing's corrupted
+ list.push(4);
+ list.push(5);
+
+ // Check normal removal
+ assert_eq!(list.pop(), Some(3));
+ assert_eq!(list.pop(), Some(4));
+
+ // Check exhaustion
+ assert_eq!(list.pop(), Some(5));
+ assert_eq!(list.pop(), None);
+
+ // Check the exhaustion case fixed the pointer right
+ list.push(6);
+ list.push(7);
+
+ // Check normal removal
+ assert_eq!(list.pop(), Some(6));
+ assert_eq!(list.pop(), Some(7));
+ assert_eq!(list.pop(), None);
+ }
+}
+```
+
+摊牌了,我们偷懒了,这些测试就是从之前的栈链表赋值过来的,但是依然做了些改变,例如在末尾增加了几个步骤以确保在 `pop` 中不会发生尾指针损坏( tail-pointer corruption )的情况。
+
+```shell
+cargo test
+
+running 12 tests
+test fifth::test::basics ... ok
+test first::test::basics ... ok
+test fourth::test::basics ... ok
+test fourth::test::peek ... ok
+test second::test::basics ... ok
+test fourth::test::into_iter ... ok
+test second::test::into_iter ... ok
+test second::test::iter ... ok
+test second::test::iter_mut ... ok
+test second::test::peek ... ok
+test third::test::basics ... ok
+test third::test::iter ... ok
+
+test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured
+```
+
diff --git a/src/too-many-lists/unsafe-queue/intro.md b/src/too-many-lists/unsafe-queue/intro.md
new file mode 100644
index 00000000..71d84046
--- /dev/null
+++ b/src/too-many-lists/unsafe-queue/intro.md
@@ -0,0 +1,20 @@
+# 不错的unsafe队列
+在之前章节中,基于内部可变性和引用计数的双向链表有些失控了,原因在于 `Rc` 和 `RefCell` 对于简单的任务而言,它们是非常称职的,但是对于复杂的任务,它们可能会变得相当笨拙,特别是当我们试图隐藏一些细节时。
+
+总之,一定有更好的办法!下面来看看该如何使用裸指针和 unsafe 代码实现一个单向链表。
+
+> 大家可能想等着看我犯错误,unsafe 嘛,不犯错误不可能的,但是呢,俺偏就不犯错误:)
+
+国际惯例,添加第五个链表所需的文件 `fifth.rs`:
+```rust
+// in lib.rs
+
+pub mod first;
+pub mod second;
+pub mod third;
+pub mod fourth;
+pub mod fifth;
+```
+
+虽然我们依然会从零开始撸代码,但是 `fifth.rs` 的代码会跟 `second.rs` 存在一定的重叠,因为对于链表而言,队列其实就是栈的增强。
+
diff --git a/src/too-many-lists/unsafe-queue/layout.md b/src/too-many-lists/unsafe-queue/layout.md
new file mode 100644
index 00000000..6ed601e6
--- /dev/null
+++ b/src/too-many-lists/unsafe-queue/layout.md
@@ -0,0 +1,374 @@
+# 数据布局
+那么单向链表的队列长什么样?对于栈来说,我们向一端推入( push )元素,然后再从同一端弹出( pop )。对于栈和队列而言,唯一的区别在于队列从末端弹出。
+
+栈的实现类似于下图:
+```shell
+input list:
+[Some(ptr)] -> (A, Some(ptr)) -> (B, None)
+
+stack push X:
+[Some(ptr)] -> (X, Some(ptr)) -> (A, Some(ptr)) -> (B, None)
+
+stack pop:
+[Some(ptr)] -> (A, Some(ptr)) -> (B, None)
+```
+
+由于队列是首端进,末端出,因此我们需要决定将 `push` 和 `pop` 中的哪个放到末端去操作,如果将 `push` 放在末端操作:
+```shell
+input list:
+[Some(ptr)] -> (A, Some(ptr)) -> (B, None)
+
+flipped push X:
+[Some(ptr)] -> (A, Some(ptr)) -> (B, Some(ptr)) -> (X, None)
+```
+
+而如果将 `pop` 放在末端:
+```shell
+input list:
+[Some(ptr)] -> (A, Some(ptr)) -> (B, Some(ptr)) -> (X, None)
+
+flipped pop:
+[Some(ptr)] -> (A, Some(ptr)) -> (B, None)
+```
+
+但是这样实现有一个很糟糕的地方:两个操作都需要遍历整个链表后才能完成。队列要求 `push` 和 `pop` 操作需要高效,但是遍历整个链表才能完成的操作怎么看都谈不上高效!
+
+其中一个解决办法就是保存一个指针指向末端:
+```rust
+use std::mem;
+
+pub struct List {
+ head: Link,
+ tail: Link, // NEW!
+}
+
+type Link = Option>>;
+
+struct Node {
+ elem: T,
+ next: Link,
+}
+
+impl List {
+ pub fn new() -> Self {
+ List { head: None, tail: None }
+ }
+
+ pub fn push(&mut self, elem: T) {
+ let new_tail = Box::new(Node {
+ elem: elem,
+ // 在尾端推入一个新节点时,新节点的下一个节点永远是 None
+ next: None,
+ });
+
+ // 让 tail 指向新的节点,并返回之前的 old tail
+ let old_tail = mem::replace(&mut self.tail, Some(new_tail));
+
+ match old_tail {
+ Some(mut old_tail) => {
+ // 若 old tail 存在,则让该节点指向新的节点
+ old_tail.next = Some(new_tail);
+ }
+ None => {
+ // 否则,将 head 指向新的节点
+ self.head = Some(new_tail);
+ }
+ }
+ }
+}
+```
+
+在之前的各种链表锤炼下,我们对于相关代码应该相当熟悉了,因此可以适当提提速 - 在写的过程中,事实上我碰到了很多错误,这些错误就不再一一列举。
+
+但是如果你担心不再能看到错误,那就纯属多余了:
+```shell
+> cargo build
+
+error[E0382]: use of moved value: `new_tail`
+ --> src/fifth.rs:38:38
+ |
+26 | let new_tail = Box::new(Node {
+ | -------- move occurs because `new_tail` has type `std::boxed::Box>`, which does not implement the `Copy` trait
+...
+33 | let old_tail = mem::replace(&mut self.tail, Some(new_tail));
+ | -------- value moved here
+...
+38 | old_tail.next = Some(new_tail);
+ | ^^^^^^^^ value used here after move
+```
+
+新鲜出炉的错误,接好!`Box` 并没有实现 `Copy` 特征,因此我们不能在两个地方进行赋值。好在,可以使用没有所有权的引用类型:
+```rust
+pub struct List {
+ head: Link,
+ tail: Option<&mut Node>, // NEW!
+}
+
+type Link = Option>>;
+
+struct Node {
+ elem: T,
+ next: Link,
+}
+
+impl List {
+ pub fn new() -> Self {
+ List { head: None, tail: None }
+ }
+
+ pub fn push(&mut self, elem: T) {
+ let new_tail = Box::new(Node {
+ elem: elem,
+ next: None,
+ });
+
+ let new_tail = match self.tail.take() {
+ Some(old_tail) => {
+ old_tail.next = Some(new_tail);
+ old_tail.next.as_deref_mut()
+ }
+ None => {
+ self.head = Some(new_tail);
+ self.head.as_deref_mut()
+ }
+ };
+
+ self.tail = new_tail;
+ }
+}
+```
+
+```shell
+> cargo build
+
+error[E0106]: missing lifetime specifier
+ --> src/fifth.rs:3:18
+ |
+3 | tail: Option<&mut Node>, // NEW!
+ | ^ expected lifetime parameter
+```
+
+好吧,结构体中的引用类型需要显式的标注生命周期,先加一个 `'a` 吧:
+```rust
+pub struct List<'a, T> {
+ head: Link,
+ tail: Option<&'a mut Node>, // NEW!
+}
+
+type Link = Option>>;
+
+struct Node {
+ elem: T,
+ next: Link,
+}
+
+impl<'a, T> List<'a, T> {
+ pub fn new() -> Self {
+ List { head: None, tail: None }
+ }
+
+ pub fn push(&mut self, elem: T) {
+ let new_tail = Box::new(Node {
+ elem: elem,
+ next: None,
+ });
+
+ let new_tail = match self.tail.take() {
+ Some(old_tail) => {
+ old_tail.next = Some(new_tail);
+ old_tail.next.as_deref_mut()
+ }
+ None => {
+ self.head = Some(new_tail);
+ self.head.as_deref_mut()
+ }
+ };
+
+ self.tail = new_tail;
+ }
+}
+```
+
+```shell
+cargo build
+
+error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
+ --> src/fifth.rs:35:27
+ |
+35 | self.head.as_deref_mut()
+ | ^^^^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 18:5...
+ --> src/fifth.rs:18:5
+ |
+18 | / pub fn push(&mut self, elem: T) {
+19 | | let new_tail = Box::new(Node {
+20 | | elem: elem,
+21 | | // When you push onto the tail, your next is always None
+... |
+39 | | self.tail = new_tail;
+40 | | }
+ | |_____^
+note: ...so that reference does not outlive borrowed content
+ --> src/fifth.rs:35:17
+ |
+35 | self.head.as_deref_mut()
+ | ^^^^^^^^^
+note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 13:6...
+ --> src/fifth.rs:13:6
+ |
+13 | impl<'a, T> List<'a, T> {
+ | ^^
+ = note: ...so that the expression is assignable:
+ expected std::option::Option<&'a mut fifth::Node>
+ found std::option::Option<&mut fifth::Node>
+```
+
+好长... Rust 为啥这么难... 但是,这里有一句重点:
+
+> the lifetime must be valid for the lifetime 'a as defined on the impl
+
+意思是说生命周期至少要和 `'a` 一样长,是不是因为编译器为 `self` 推导的生命周期不够长呢?我们试着来手动标注下:
+```rust
+pub fn push(&'a mut self, elem: T) {
+```
+
+当当当当,成功通过编译:
+```shell
+cargo build
+
+warning: field is never used: `elem`
+ --> src/fifth.rs:9:5
+ |
+9 | elem: T,
+ | ^^^^^^^
+ |
+ = note: #[warn(dead_code)] on by default
+```
+
+这个错误可以称之为错误之王,但是我们依然成功的解决了它,太棒了!再来实现下 `pop`:
+```rust
+pub fn pop(&'a mut self) -> Option {
+ self.head.take().map(|head| {
+ let head = *head;
+ self.head = head.next;
+
+ if self.head.is_none() {
+ self.tail = None;
+ }
+
+ head.elem
+ })
+}
+```
+
+看起来不错,写几个测试用例溜一溜:
+```rust
+mod test {
+ use super::List;
+ #[test]
+ fn basics() {
+ let mut list = List::new();
+
+ // Check empty list behaves right
+ assert_eq!(list.pop(), None);
+
+ // Populate list
+ list.push(1);
+ list.push(2);
+ list.push(3);
+
+ // Check normal removal
+ assert_eq!(list.pop(), Some(1));
+ assert_eq!(list.pop(), Some(2));
+
+ // Push some more just to make sure nothing's corrupted
+ list.push(4);
+ list.push(5);
+
+ // Check normal removal
+ assert_eq!(list.pop(), Some(3));
+ assert_eq!(list.pop(), Some(4));
+
+ // Check exhaustion
+ assert_eq!(list.pop(), Some(5));
+ assert_eq!(list.pop(), None);
+ }
+}
+```
+```shell
+cargo test
+
+error[E0499]: cannot borrow `list` as mutable more than once at a time
+ --> src/fifth.rs:68:9
+ |
+65 | assert_eq!(list.pop(), None);
+ | ---- first mutable borrow occurs here
+...
+68 | list.push(1);
+ | ^^^^
+ | |
+ | second mutable borrow occurs here
+ | first borrow later used here
+
+error[E0499]: cannot borrow `list` as mutable more than once at a time
+ --> src/fifth.rs:69:9
+ |
+65 | assert_eq!(list.pop(), None);
+ | ---- first mutable borrow occurs here
+...
+69 | list.push(2);
+ | ^^^^
+ | |
+ | second mutable borrow occurs here
+ | first borrow later used here
+
+error[E0499]: cannot borrow `list` as mutable more than once at a time
+ --> src/fifth.rs:70:9
+ |
+65 | assert_eq!(list.pop(), None);
+ | ---- first mutable borrow occurs here
+...
+70 | list.push(3);
+ | ^^^^
+ | |
+ | second mutable borrow occurs here
+ | first borrow later used here
+
+
+....
+
+** WAY MORE LINES OF ERRORS **
+
+....
+
+error: aborting due to 11 previous errors
+```
+
+🙀🙀🙀,震惊!但编译器真的没错,因为都是我们刚才那个标记惹的祸。
+
+我们为 `self` 标记了 `'a`,意味着在 `'a` 结束前,无法再去使用 `self`,大家可以自己推断下 `'a` 的生命周期是多长。
+
+那么该怎么办?回到老路 `RefCell` 上?显然不可能,那只能祭出大杀器:裸指针。
+
+> 事实上,上文的问题主要是自引用引起的,感兴趣的同学可以查看[这里](https://course.rs/advance/circle-self-ref/intro.html)深入阅读。
+
+```rust
+pub struct List {
+ head: Link,
+ tail: *mut Node, // DANGER DANGER
+}
+
+type Link = Option>>;
+
+struct Node {
+ elem: T,
+ next: Link,
+}
+```
+
+如上所示,当使用裸指针后, `head` 和 `tail` 就不会形成自引用的问题,也不再违反 Rust 严苛的借用规则。
+
+> 注意!当前的实现依然是有严重问题的,在后面我们会修复
+
+果然,编程的最高境界就是回归本质:使用 C 语言的东东。
\ No newline at end of file
diff --git a/src/too-many-lists/unsafe-queue/miri.md b/src/too-many-lists/unsafe-queue/miri.md
new file mode 100644
index 00000000..0cb65bf5
--- /dev/null
+++ b/src/too-many-lists/unsafe-queue/miri.md
@@ -0,0 +1,175 @@
+# Miri
+看到这里,大家是不是暗中松了口气?unsafe 不过如此嘛,不知道为何其它人都谈之色变。
+
+怎么说呢?你以为的编译器已经不是以前的编译器了,它不报错不代表没有错误。包括测试用例也是,正常地运行不能意味着代码没有任何错误。
+
+在周星驰电影功夫中,还有一个奇怪大叔 10 元一本主动上门卖如来神掌,那么有没有 10 元一本的 Rust 秘笈呢?( 喂,Rust语言圣经都免费让你读了,有了摩托车,还要什么拖拉机... 哈哈,开个玩笑 )
+
+有的,奇怪大叔正在赶来,他告诉我们先来安装一个命令:
+```shell
+rustup +nightly-2022-01-21 component add miri
+info: syncing channel updates for 'nightly-2022-01-21-x86_64-pc-windows-msvc'
+info: latest update on 2022-01-21, rust version 1.60.0-nightly (777bb86bc 2022-01-20)
+info: downloading component 'cargo'
+info: downloading component 'clippy'
+info: downloading component 'rust-docs'
+info: downloading component 'rust-std'
+info: downloading component 'rustc'
+info: downloading component 'rustfmt'
+info: installing component 'cargo'
+info: installing component 'clippy'
+info: installing component 'rust-docs'
+info: installing component 'rust-std'
+info: installing component 'rustc'
+info: installing component 'rustfmt'
+info: downloading component 'miri'
+info: installing component 'miri'
+```
+
+等等,你在我电脑上装了什么奇怪的东西?! "好东西"
+
+
+> miri 目前只能在 nightly Rust 上安装,`+nightly-2022-01-21` 告诉 `rustup` 我们想要安装的 `nightly` 版本,事实上,你可以直接通过 `rustup +nightly component add miri` 安装,这里指定版本主要因为 `miri` 有时候会因为某些版本而出错。
+>
+> 2022-01-21 是我所知的 miri 可以成功运行的版本,你可以检查[这个网址](https://rust-lang.github.io/rustup-components-history/)获取更多信息
+>
+> + 是一种临时性的规则运用,如果你不想每次都使用 `+nightly-2022-01-21`,可以使用 [`rustup override set`](https://course.rs/appendix/rust-version.html#rustup-和-rust-nightly-的职责) 命令对当前项目的 Rust 版本进行覆盖
+
+```shell
+> cargo +nightly-2022-01-21 miri test
+
+I will run `"cargo.exe" "install" "xargo"` to install
+a recent enough xargo. Proceed? [Y/n]
+```
+
+额,`xargo` 是什么东东?"不要担心,选择 y 就行,我像是会坑你的人吗?"
+
+```shell
+> y
+
+ Updating crates.io index
+ Installing xargo v0.3.24
+...
+ Finished release [optimized] target(s) in 10.65s
+ Installing C:\Users\ninte\.cargo\bin\xargo-check.exe
+ Installing C:\Users\ninte\.cargo\bin\xargo.exe
+ Installed package `xargo v0.3.24` (executables `xargo-check.exe`, `xargo.exe`)
+
+I will run `"rustup" "component" "add" "rust-src"` to install
+the `rust-src` component for the selected toolchain. Proceed? [Y/n]
+```
+
+额? "不要怕,多给你一份 Rust 源代码,不开心嘛?"
+
+```shell
+> y
+
+info: downloading component 'rust-src'
+info: installing component 'rust-src'
+```
+
+"看吧,我就说我不会骗你的,不相信我,等着错误砸脸吧!" 真是一个奇怪的大叔...
+```shell
+ Compiling lists v0.1.0 (C:\Users\ninte\dev\tmp\lists)
+ Finished test [unoptimized + debuginfo] target(s) in 0.25s
+ Running unittests (lists-5cc11d9ee5c3e924.exe)
+
+error: Undefined Behavior: trying to reborrow for Unique at alloc84055,
+ but parent tag <209678> does not have an appropriate item in
+ the borrow stack
+
+ --> \lib\rustlib\src\rust\library\core\src\option.rs:846:18
+ |
+846 | Some(x) => Some(f(x)),
+ | ^ trying to reborrow for Unique at alloc84055,
+ | but parent tag <209678> does not have an
+ | appropriate item in the borrow stack
+ |
+ = help: this indicates a potential bug in the program:
+ it performed an invalid operation, but the rules it
+ violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md
+ for further information
+
+ = note: inside `std::option::Option::>>::map::` at \lib\rustlib\src\rust\library\core\src\option.rs:846:18
+
+note: inside `fifth::List::::pop` at src\fifth.rs:31:9
+ --> src\fifth.rs:31:9
+ |
+31 | / self.head.take().map(|head| {
+32 | | let head = *head;
+33 | | self.head = head.next;
+34 | |
+... |
+39 | | head.elem
+40 | | })
+ | |__________^
+note: inside `fifth::test::basics` at src\fifth.rs:74:20
+ --> src\fifth.rs:74:20
+ |
+74 | assert_eq!(list.pop(), Some(1));
+ | ^^^^^^^^^^
+note: inside closure at src\fifth.rs:62:5
+ --> src\fifth.rs:62:5
+ |
+61 | #[test]
+ | ------- in this procedural macro expansion
+62 | / fn basics() {
+63 | | let mut list = List::new();
+64 | |
+65 | | // Check empty list behaves right
+... |
+96 | | assert_eq!(list.pop(), None);
+97 | | }
+ | |_____^
+ ...
+error: aborting due to previous error
+```
+
+咦还真有错误,大叔,这是什么错误?大叔?...奇怪的大叔默默离开了,留下我在风中凌乱。
+
+果然不靠谱...还是得靠自己,首先得了解下何为 `miri`。
+
+
+[`miri`](https://github.com/rust-lang/miri) 可以生成 Rust 的中间层表示 MIR,对于编译器来说,我们的 Rust 代码首先会被编译为 MIR ,然后再提交给 LLVM 进行处理。
+
+可以通过 `rustup component add miri` 来安装它,并通过 `cargo miri` 来使用,同时还可以使用 `cargo miri test` 来运行测试代码。
+
+`miri` 可以帮助我们检查常见的未定义行为(UB = Undefined Behavior),以下列出了一部分:
+
+- 内存越界检查和内存释放后再使用(use-after-free)
+- 使用未初始化的数据
+- 数据竞争
+- 内存对齐问题
+
+UB 检测是必须的,因为它发生在运行时,因此很难发现,如果 `miri` 能在编译期检测出来,那自然是最好不过的。
+
+总之,`miri` 的使用很简单:
+```shell
+> cargo +nightly-2022-01-21 miri test
+```
+
+下面来看看具体的错误:
+```shell
+error: Undefined Behavior: trying to reborrow for Unique at alloc84055, but parent tag <209678> does not have an appropriate item in the borrow stack
+
+ --> \lib\rustlib\src\rust\library\core\src\option.rs:846:18
+ |
+846 | Some(x) => Some(f(x)),
+ | ^ trying to reborrow for Unique at alloc84055,
+ | but parent tag <209678> does not have an
+ | appropriate item in the borrow stack
+ |
+
+ = help: this indicates a potential bug in the program: it
+ performed an invalid operation, but the rules it
+ violated are still experimental
+
+ = help: see
+ https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md
+ for further information
+```
+
+嗯,只能看出是一个错误,其它完全看不懂了,例如什么是 `borrow stack`?
+
+
diff --git a/src/cargo/getting-started.md b/src/toolchains/cargo/getting-started.md
similarity index 100%
rename from src/cargo/getting-started.md
rename to src/toolchains/cargo/getting-started.md
diff --git a/src/cargo/git-auth.md b/src/toolchains/cargo/git-auth.md
similarity index 100%
rename from src/cargo/git-auth.md
rename to src/toolchains/cargo/git-auth.md
diff --git a/src/cargo/guide/build-cache.md b/src/toolchains/cargo/guide/build-cache.md
similarity index 80%
rename from src/cargo/guide/build-cache.md
rename to src/toolchains/cargo/guide/build-cache.md
index 2eddbc83..7b5a4d63 100644
--- a/src/cargo/guide/build-cache.md
+++ b/src/toolchains/cargo/guide/build-cache.md
@@ -1,6 +1,6 @@
# 构建( Build )缓存
-`cargo build` 的结果会被放入项目根目录下的 `target` 文件夹中,当然,这个位置可以三种方式更改:设置 `CARGO_TARGET_DIR` [环境变量](https://doc.rust-lang.org/stable/cargo/reference/environment-variables.html)、[`build.target-dir`](https://course.rs/cargo/reference/configuration.html#配置文件概览) 配置项以及 `--target-dir` 命令行参数。
+`cargo build` 的结果会被放入项目根目录下的 `target` 文件夹中,当然,这个位置可以三种方式更改:设置 `CARGO_TARGET_DIR` [环境变量](https://doc.rust-lang.org/stable/cargo/reference/environment-variables.html)、[`build.target-dir`](https://course.rs/toolchains/cargo/reference/configuration.html#配置文件概览) 配置项以及 `--target-dir` 命令行参数。
## target 目录结构
@@ -8,7 +8,7 @@
#### 不使用 --target
-若 `--target` 标志没有指定,`Cargo` 会根据宿主机架构进行构建,构建结果会放入项目根目录下的 `target` 目录中,`target` 下每个子目录中包含了相应的 [`发布配置profile`](https://course.rs/cargo/reference/profiles.html) 的构建结果,例如 `release`、`debug` 是自带的`profile`,前者往往用于生产环境,因为会做大量的性能优化,而后者则用于开发环境,此时的编译效率和报错信息是最好的。
+若 `--target` 标志没有指定,`Cargo` 会根据宿主机架构进行构建,构建结果会放入项目根目录下的 `target` 目录中,`target` 下每个子目录中包含了相应的 [`发布配置profile`](https://course.rs/toolchains/cargo/reference/profiles.html) 的构建结果,例如 `release`、`debug` 是自带的`profile`,前者往往用于生产环境,因为会做大量的性能优化,而后者则用于开发环境,此时的编译效率和报错信息是最好的。
除此之外我们还可以定义自己想要的 `profile` ,例如用于测试环境的 `profile`: `test`,用于预发环境的 `profile` :`pre-prod` 等。
@@ -33,7 +33,7 @@
| `target//debug/` | `target/thumbv7em-none-eabihf/debug/` |
| `target//release/` | `target/thumbv7em-none-eabihf/release/` |
-> **注意:**,当没有使用 `--target` 时,`Cargo` 会与构建脚本和过程宏一起共享你的依赖包,对于每个 `rustc` 命令调用而言,[`RUSTFLAGS`](https://course.rs/cargo/reference/configuration.html#配置文件概览) 也将被共享。
+> **注意:**,当没有使用 `--target` 时,`Cargo` 会与构建脚本和过程宏一起共享你的依赖包,对于每个 `rustc` 命令调用而言,[`RUSTFLAGS`](https://course.rs/toolchains/cargo/reference/configuration.html#配置文件概览) 也将被共享。
>
> 而使用 `--target` 后,构建脚本、过程宏会针对宿主机的 CPU 架构进行各自构建,且不会共享 `RUSTFLAGS`。
@@ -43,8 +43,8 @@
| 目录 | 描述 |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
-| `target/debug/` | 包含编译后的输出,例如二进制可执行文件、[库对象( library target )](https://course.rs/cargo/reference/cargo-target.html#库对象library) |
-| `target/debug/examples/` | 包含[示例对象( example target )](https://course.rs/cargo/reference/cargo-target.html#示例对象examples) |
+| `target/debug/` | 包含编译后的输出,例如二进制可执行文件、[库对象( library target )](https://course.rs/toolchains/cargo/reference/cargo-target.html#库对象library) |
+| `target/debug/examples/` | 包含[示例对象( example target )](https://course.rs/toolchains/cargo/reference/cargo-target.html#示例对象examples) |
还有一些命令会在 `target` 下生成自己的独立目录:
@@ -58,8 +58,8 @@ Cargo 还会创建几个用于构建过程的其它类型目录,它们的目
| 目录 | 描述 |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `target/debug/deps` | 依赖和其它输出成果 |
-| `target/debug/incremental` | `rustc` [增量编译](https://course.rs/cargo/reference/profiles.html#incremental)的输出,该缓存可以用于提升后续的编译速度 |
-| `target/debug/build/` | [构建脚本](https://course.rs/cargo/reference/build-script/intro.html)的输出 |
+| `target/debug/incremental` | `rustc` [增量编译](https://course.rs/toolchains/cargo/reference/profiles.html#incremental)的输出,该缓存可以用于提升后续的编译速度 |
+| `target/debug/build/` | [构建脚本](https://course.rs/toolchains/cargo/reference/build-script/intro.html)的输出 |
## 依赖信息文件
@@ -67,7 +67,7 @@ Cargo 还会创建几个用于构建过程的其它类型目录,它们的目
该文件往往用于提供给外部的构建系统,这样它们就可以判断 `Cargo` 命令是否需要再次被执行。
-文件中的路径默认是绝对路径,你可以通过 [`build.dep-info-basedir`](https://course.rs/cargo/reference/configuration.html#配置文件概览) 配置项来修改为相对路径。
+文件中的路径默认是绝对路径,你可以通过 [`build.dep-info-basedir`](https://course.rs/toolchains/cargo/reference/configuration.html#配置文件概览) 配置项来修改为相对路径。
```shell
# 关于 `.d` 文件的一个示例 : target/debug/foo.d
@@ -81,4 +81,4 @@ Cargo 还会创建几个用于构建过程的其它类型目录,它们的目
为了设置 `sccache`,首先需要使用 `cargo install sccache` 进行安装,然后在调用 `Cargo` 之前将 `RUSTC_WRAPPER` 环境变量设置为 `sccache`。
- 如果用的 `bash`,可以将 `export RUSTC_WRAPPER=sccache` 添加到 `.bashrc` 中
-- 也可以使用 [`build.rustc-wrapper`](https://course.rs/cargo/reference/configuration.html#配置文件概览) 配置项
+- 也可以使用 [`build.rustc-wrapper`](https://course.rs/toolchains/cargo/reference/configuration.html#配置文件概览) 配置项
diff --git a/src/cargo/guide/cargo-cache.md b/src/toolchains/cargo/guide/cargo-cache.md
similarity index 95%
rename from src/cargo/guide/cargo-cache.md
rename to src/toolchains/cargo/guide/cargo-cache.md
index 831a61fc..5bec0313 100644
--- a/src/cargo/guide/cargo-cache.md
+++ b/src/toolchains/cargo/guide/cargo-cache.md
@@ -17,7 +17,7 @@ $ echo $HOME/.cargo/
## 文件
-- `config.toml` 是 Cargo 的全局配置文件,具体请查看[这里](https://course.rs/cargo/reference/configuration.html)
+- `config.toml` 是 Cargo 的全局配置文件,具体请查看[这里](https://course.rs/toolchains/cargo/reference/configuration.html)
- `credentials.toml` 为 `cargo login` 提供私有化登录证书,用于登录 `package` 注册中心,例如 `crates.io`
- `.crates.toml`, `.crates2.json` 这两个是隐藏文件,包含了通过 `cargo install` 安装的包的 `package` 信息,**请不要手动修改!**
@@ -65,7 +65,7 @@ $ echo $HOME/.cargo/
解决办法很简单:
-- 既然下载慢,那就使用[国内的注册服务](https://course.rs/cargo/reference/specify-deps.html#从其它注册服务引入依赖包),不再使用 crates.io
+- 既然下载慢,那就使用[国内的注册服务](https://course.rs/toolchains/cargo/reference/specify-deps.html#从其它注册服务引入依赖包),不再使用 crates.io
- 耐心等待持有锁的用户构建完成
- 强行停止正在构建的进程,例如杀掉 IDE 使用的 rust-analyer 插件进程,然后删除 `$HOME/.cargo/.package_cache` 目录
diff --git a/src/cargo/guide/cargo-toml-lock.md b/src/toolchains/cargo/guide/cargo-toml-lock.md
similarity index 100%
rename from src/cargo/guide/cargo-toml-lock.md
rename to src/toolchains/cargo/guide/cargo-toml-lock.md
diff --git a/src/cargo/guide/dependencies.md b/src/toolchains/cargo/guide/dependencies.md
similarity index 95%
rename from src/cargo/guide/dependencies.md
rename to src/toolchains/cargo/guide/dependencies.md
index 851296b5..5ae02ddb 100644
--- a/src/cargo/guide/dependencies.md
+++ b/src/toolchains/cargo/guide/dependencies.md
@@ -9,7 +9,7 @@
time = "0.1.12"
```
-可以看到我们指定了 `time` 包的版本号 "0.1.12",关于版本号,实际上还有其它的指定方式,具体参见[指定依赖项](https://course.rs/cargo/reference/specify-deps.html)章节。
+可以看到我们指定了 `time` 包的版本号 "0.1.12",关于版本号,实际上还有其它的指定方式,具体参见[指定依赖项](https://course.rs/toolchains/cargo/reference/specify-deps.html)章节。
如果想继续添加 `regexp` 包,只需在 `time` 包后面添加即可 :
diff --git a/src/cargo/guide/download-package.md b/src/toolchains/cargo/guide/download-package.md
similarity index 100%
rename from src/cargo/guide/download-package.md
rename to src/toolchains/cargo/guide/download-package.md
diff --git a/src/cargo/guide/intro.md b/src/toolchains/cargo/guide/intro.md
similarity index 100%
rename from src/cargo/guide/intro.md
rename to src/toolchains/cargo/guide/intro.md
diff --git a/src/cargo/guide/package-layout.md b/src/toolchains/cargo/guide/package-layout.md
similarity index 95%
rename from src/cargo/guide/package-layout.md
rename to src/toolchains/cargo/guide/package-layout.md
index a0b9f336..653adcc3 100644
--- a/src/cargo/guide/package-layout.md
+++ b/src/toolchains/cargo/guide/package-layout.md
@@ -45,5 +45,5 @@
关于 Rust 中的包和模块,[之前的章节](https://course.rs/basic/crate-module/intro.html)有更详细的解释。
-此外,`bin`、`tests`、`examples` 等目录路径都可以通过配置文件进行配置,它们被统一称之为 [Cargo Target](https://course.rs/cargo/reference/cargo-target.html)。
+此外,`bin`、`tests`、`examples` 等目录路径都可以通过配置文件进行配置,它们被统一称之为 [Cargo Target](https://course.rs/toolchains/cargo/reference/cargo-target.html)。
diff --git a/src/cargo/guide/tests-ci.md b/src/toolchains/cargo/guide/tests-ci.md
similarity index 100%
rename from src/cargo/guide/tests-ci.md
rename to src/toolchains/cargo/guide/tests-ci.md
diff --git a/src/cargo/guide/why-exist.md b/src/toolchains/cargo/guide/why-exist.md
similarity index 100%
rename from src/cargo/guide/why-exist.md
rename to src/toolchains/cargo/guide/why-exist.md
diff --git a/src/cargo/intro.md b/src/toolchains/cargo/intro.md
similarity index 100%
rename from src/cargo/intro.md
rename to src/toolchains/cargo/intro.md
diff --git a/src/cargo/reference/build-script/examples.md b/src/toolchains/cargo/reference/build-script/examples.md
similarity index 97%
rename from src/cargo/reference/build-script/examples.md
rename to src/toolchains/cargo/reference/build-script/examples.md
index cdba5d5f..9a7694de 100644
--- a/src/cargo/reference/build-script/examples.md
+++ b/src/toolchains/cargo/reference/build-script/examples.md
@@ -250,7 +250,7 @@ fn test_crc32() {
}
```
-代码很清晰,也很简洁,这里就不再过多介绍,运行 [`cargo build --vv`](https://course.rs/cargo/reference/build-script/intro.html#构建脚本的输出) 来看看部分结果( 系统中需要已经安装 `libz` 库):
+代码很清晰,也很简洁,这里就不再过多介绍,运行 [`cargo build --vv`](https://course.rs/toolchains/cargo/reference/build-script/intro.html#构建脚本的输出) 来看看部分结果( 系统中需要已经安装 `libz` 库):
```shell
[libz-sys 0.1.0] cargo:rustc-link-search=native=/usr/lib
@@ -268,7 +268,7 @@ fn test_crc32() {
若你有一个依赖于 `zlib` 的库,那可以使用 `libz-sys` 来自动发现或构建该库。这个功能对于交叉编译非常有用,例如 Windows 下往往不会安装 `zlib`。
-`libz-sys` 通过设置 [`include`](https://github.com/rust-lang/libz-sys/blob/3c594e677c79584500da673f918c4d2101ac97a1/build.rs#L156) 元数据来告知其它包去哪里找到 `zlib` 的头文件,然后我们的构建脚本可以通过 `DEP_Z_INCLUDE` 环境变量来读取 `include` 元数据( 关于元数据的传递,见[这里](https://course.rs/cargo/reference/build-script/intro.html#links) )。
+`libz-sys` 通过设置 [`include`](https://github.com/rust-lang/libz-sys/blob/3c594e677c79584500da673f918c4d2101ac97a1/build.rs#L156) 元数据来告知其它包去哪里找到 `zlib` 的头文件,然后我们的构建脚本可以通过 `DEP_Z_INCLUDE` 环境变量来读取 `include` 元数据( 关于元数据的传递,见[这里](https://course.rs/toolchains/cargo/reference/build-script/intro.html#links) )。
```toml
# Cargo.toml
diff --git a/src/cargo/reference/build-script/intro.md b/src/toolchains/cargo/reference/build-script/intro.md
similarity index 95%
rename from src/cargo/reference/build-script/intro.md
rename to src/toolchains/cargo/reference/build-script/intro.md
index edd2842a..e2653d0f 100644
--- a/src/cargo/reference/build-script/intro.md
+++ b/src/toolchains/cargo/reference/build-script/intro.md
@@ -28,9 +28,9 @@ fn main() {
- 根据某个说明描述文件生成一个 Rust 模块
- 执行一些平台相关的配置
-下面的部分我们一起来看看构建脚本具体是如何工作的,然后在[下个章节](https://course.rs/cargo/reference/build-script/examples.html)中还提供了一些关于如何编写构建脚本的示例。
+下面的部分我们一起来看看构建脚本具体是如何工作的,然后在[下个章节](https://course.rs/toolchains/cargo/reference/build-script/examples.html)中还提供了一些关于如何编写构建脚本的示例。
-> Note: [`package.build`](https://course.rs/cargo/reference/manifest.html#build) 可以用于改变构建脚本的名称,或者直接禁用该功能
+> Note: [`package.build`](https://course.rs/toolchains/cargo/reference/manifest.html#build) 可以用于改变构建脚本的名称,或者直接禁用该功能
#### 构建脚本的生命期
@@ -121,7 +121,7 @@ Cargo 要求一个本地库最多只能被一个项目所链接,换而言之
## 覆盖构建脚本
-当 `Cargo.toml` 设置了 `links` 时, Cargo 就允许我们使用自定义库对现有的构建脚本进行覆盖。在 [Cargo 使用的配置文件](https://course.rs/cargo/reference/configuration.html)中添加以下内容:
+当 `Cargo.toml` 设置了 `links` 时, Cargo 就允许我们使用自定义库对现有的构建脚本进行覆盖。在 [Cargo 使用的配置文件](https://course.rs/toolchains/cargo/reference/configuration.html)中添加以下内容:
```toml
[target.x86_64-unknown-linux-gnu.foo]
diff --git a/src/cargo/reference/cargo-target.md b/src/toolchains/cargo/reference/cargo-target.md
similarity index 93%
rename from src/cargo/reference/cargo-target.md
rename to src/toolchains/cargo/reference/cargo-target.md
index 609ca316..952a73a1 100644
--- a/src/cargo/reference/cargo-target.md
+++ b/src/toolchains/cargo/reference/cargo-target.md
@@ -1,6 +1,6 @@
# Cargo Target
-**Cargo 项目中包含有一些对象,它们包含的源代码文件可以被编译成相应的包,这些对象被称之为 Cargo Target**。例如[之前章节](https://course.rs/cargo/guide/package-layout.html)提到的库对象 `Library` 、二进制对象 `Binary`、示例对象 `Examples`、测试对象 `Tests` 和 基准性能对象 `Benches` 都是 Cargo Target。
+**Cargo 项目中包含有一些对象,它们包含的源代码文件可以被编译成相应的包,这些对象被称之为 Cargo Target**。例如[之前章节](https://course.rs/toolchains/cargo/guide/package-layout.html)提到的库对象 `Library` 、二进制对象 `Binary`、示例对象 `Examples`、测试对象 `Tests` 和 基准性能对象 `Benches` 都是 Cargo Target。
本章节我们一起来看看该如何在 `Cargo.toml` 清单中配置这些对象,当然,大部分时候都无需手动配置,因为默认的配置通常由项目目录的布局自动推断出来。
@@ -137,7 +137,7 @@ required-features = [] # 构建对象所需的 Cargo Features (N/A for lib).
#### required-features
-该字段用于指定在构建对象时所需的 [`features`](https://course.rs/cargo/reference/features.html) 列表。
+该字段用于指定在构建对象时所需的 [`features`](https://course.rs/toolchains/cargo/reference/features.html) 列表。
该字段只对 `[[bin]]`、 `[[bench]]`、 `[[test]]` 和 `[[example]]` 有效,对于 `[lib]` 没有任何效果。
@@ -155,7 +155,7 @@ required-features = ["postgres", "tools"]
## 对象自动发现
-默认情况下,`Cargo` 会基于项目的[目录文件布局](https://course.rs/cargo/guide/package-layout.html)自动发现和确定对象,而之前的配置项则允许我们对其进行手动的配置修改(若项目布局跟标准的不一样时)。
+默认情况下,`Cargo` 会基于项目的[目录文件布局](https://course.rs/toolchains/cargo/guide/package-layout.html)自动发现和确定对象,而之前的配置项则允许我们对其进行手动的配置修改(若项目布局跟标准的不一样时)。
而这种自动发现对象的设定可以通过以下配置来禁用:
diff --git a/src/cargo/reference/configuration.md b/src/toolchains/cargo/reference/configuration.md
similarity index 95%
rename from src/cargo/reference/configuration.md
rename to src/toolchains/cargo/reference/configuration.md
index 8b531482..c29febe4 100644
--- a/src/cargo/reference/configuration.md
+++ b/src/toolchains/cargo/reference/configuration.md
@@ -1,6 +1,6 @@
# 通过 config.toml 对 Cargo 进行配置
-Cargo 相关的配置有两种,第一种是对自身进行配置,第二种是对指定的项目进行配置,关于后者请查看 [Cargo.toml 清单](https://course.rs/cargo/reference/manifest.html)。对于普通用户而言第二种才是我们最常使用的。
+Cargo 相关的配置有两种,第一种是对自身进行配置,第二种是对指定的项目进行配置,关于后者请查看 [Cargo.toml 清单](https://course.rs/toolchains/cargo/reference/manifest.html)。对于普通用户而言第二种才是我们最常使用的。
本文讲述的是如何对 Cargo 相关的工具进行配置,该配置中的部分内容可能会覆盖掉 `Cargo.toml` 中对应的部分,例如关于 `profile` 的内容。
@@ -95,7 +95,7 @@ offline = true # 不能访问网络
[patch.]
# Same keys as for [patch] in Cargo.toml
-[profile.] # profile 配置,详情见"如何在 Cargo.toml 中配置 profile" : https://course.rs/cargo/reference/profiles.html#profile设置
+[profile.] # profile 配置,详情见"如何在 Cargo.toml 中配置 profile" : https://course.rs/toolchains/cargo/reference/profiles.html#profile设置
opt-level = 0
debug = true
split-debuginfo = '...'
@@ -109,7 +109,7 @@ rpath = false
[profile..build-override]
[profile..package.]
-[registries.] # 设置其它的注册服务: https://course.rs/cargo/reference/specify-deps.html#从其它注册服务引入依赖包
+[registries.] # 设置其它的注册服务: https://course.rs/toolchains/cargo/reference/specify-deps.html#从其它注册服务引入依赖包
index = "…" # 注册服务索引列表的 URL
token = "…" # 连接注册服务所需的鉴权 token
diff --git a/src/cargo/reference/deps-overriding.md b/src/toolchains/cargo/reference/deps-overriding.md
similarity index 93%
rename from src/cargo/reference/deps-overriding.md
rename to src/toolchains/cargo/reference/deps-overriding.md
index 74466d7c..9129578c 100644
--- a/src/cargo/reference/deps-overriding.md
+++ b/src/toolchains/cargo/reference/deps-overriding.md
@@ -9,7 +9,7 @@
下面我们来具体看看类似的问题该如何解决。
-> 上一章节中我们讲了如果通过[多种引用方式](https://course.rs/cargo/reference/specify-deps/intro.html#多引用方式混合)来引入一个包,其实这也是一种依赖覆盖。
+> 上一章节中我们讲了如果通过[多种引用方式](https://course.rs/toolchains/cargo/reference/specify-deps/intro.html#多引用方式混合)来引入一个包,其实这也是一种依赖覆盖。
## 测试 bugfix 版本
@@ -53,7 +53,7 @@ what is locked in the Cargo.lock file, run `cargo update` to use the new
version. This may also occur with an optional dependency that is not enabled.
```
-具体原因比较复杂,但是仔细观察,会发现克隆下来的 `uuid` 的版本是 `v1.0.0-alpha.1` (在 `"../uuid/Cargo.toml"` 中可以查看),然后我们本地引入的 `uuid` 版本是 `0.8.2`,根据之前讲过的 `crates.io` 的[版本规则](https://course.rs/cargo/reference/specify-deps/intro.html#从-cratesio-引入依赖包),这两者是不兼容的,`0.8.2` 只能升级到 `0.8.z`,例如 `0.8.3`。
+具体原因比较复杂,但是仔细观察,会发现克隆下来的 `uuid` 的版本是 `v1.0.0-alpha.1` (在 `"../uuid/Cargo.toml"` 中可以查看),然后我们本地引入的 `uuid` 版本是 `0.8.2`,根据之前讲过的 `crates.io` 的[版本规则](https://course.rs/toolchains/cargo/reference/specify-deps/intro.html#从-cratesio-引入依赖包),这两者是不兼容的,`0.8.2` 只能升级到 `0.8.z`,例如 `0.8.3`。
既然如此,我们先将 "../uuid/Cargo.toml" 中的 `version = "1.0.0-alpha.1"` 修改为 `version = "0.8.3"` ,然后看看结果先:
@@ -173,7 +173,7 @@ uuid = { git = 'https://github.com/uuid-rs/uuid', branch = '2.0.0' }
## 多版本[patch]
-在之前章节,我们介绍过如何使用 `package key` 来[重命名依赖包](https://course.rs/cargo/reference/specify-deps/intro.html#在-cargotoml-中重命名依赖),现在来看看如何使用它同时引入多个 `patch`。
+在之前章节,我们介绍过如何使用 `package key` 来[重命名依赖包](https://course.rs/toolchains/cargo/reference/specify-deps/intro.html#在-cargotoml-中重命名依赖),现在来看看如何使用它同时引入多个 `patch`。
假设,我们对 `serde` 有两个新的 `patch` 需求:
@@ -196,7 +196,7 @@ serde2 = { git = 'https://github.com/example/serde', package = 'serde', branch =
有时我们只是临时性地对一个项目进行处理,因此并不想去修改它的 `Cargo.toml`。此时可以使用 `Cargo` 提供的路径覆盖方法: **注意,这个方法限制较多,如果可以,还是要使用 [patch]**。
-与 `[patch]` 修改 `Cargo.toml` 不同,路径覆盖修改的是 `Cargo` 自身的[配置文件](https://course.rs/cargo/guide/cargo-cache.html#cargo-home) `$Home/.cargo/config.toml`:
+与 `[patch]` 修改 `Cargo.toml` 不同,路径覆盖修改的是 `Cargo` 自身的[配置文件](https://course.rs/toolchains/cargo/guide/cargo-cache.html#cargo-home) `$Home/.cargo/config.toml`:
```toml
paths = ["/path/to/uuid"]
diff --git a/src/cargo/reference/env.md b/src/toolchains/cargo/reference/env.md
similarity index 100%
rename from src/cargo/reference/env.md
rename to src/toolchains/cargo/reference/env.md
diff --git a/src/cargo/reference/features/examples.md b/src/toolchains/cargo/reference/features/examples.md
similarity index 100%
rename from src/cargo/reference/features/examples.md
rename to src/toolchains/cargo/reference/features/examples.md
diff --git a/src/cargo/reference/features/intro.md b/src/toolchains/cargo/reference/features/intro.md
similarity index 96%
rename from src/cargo/reference/features/intro.md
rename to src/toolchains/cargo/reference/features/intro.md
index e91fa58d..03c03281 100644
--- a/src/cargo/reference/features/intro.md
+++ b/src/toolchains/cargo/reference/features/intro.md
@@ -87,7 +87,7 @@ avif = ["ravif", "rgb"]
之后,`avif` feature 一旦被启用,那这两个依赖库也将自动被引入。
-> 注意:我们之前也讲过条件引入依赖的方法,那就是使用[平台相关的依赖](https://course.rs/cargo/reference/specify-deps.html#根据平台引入依赖),与基于 feature 的可选依赖不同,它们是基于特定平台的可选依赖
+> 注意:我们之前也讲过条件引入依赖的方法,那就是使用[平台相关的依赖](https://course.rs/toolchains/cargo/reference/specify-deps.html#根据平台引入依赖),与基于 feature 的可选依赖不同,它们是基于特定平台的可选依赖
## 依赖库自身的 feature
@@ -249,11 +249,11 @@ V2 版本的解析器可以在某些情况下避免 feature 同一化的发生
## 构建脚本
-[构建脚本](https://course.rs/cargo/reference/build-script/intro.html)可以通过 `CARGO_FEATURE_` 环境变量获取启用的 `feauture` 列表,其中 `` 是 feature 的名称,该名称被转换成大全写字母,且 `-` 被转换为 `_`。
+[构建脚本](https://course.rs/toolchains/cargo/reference/build-script/intro.html)可以通过 `CARGO_FEATURE_` 环境变量获取启用的 `feauture` 列表,其中 `` 是 feature 的名称,该名称被转换成大全写字母,且 `-` 被转换为 `_`。
## required-features
-该字段可以用于禁用特定的 Cargo Target:当某个 feature 没有被启用时,查看[这里](https://course.rs/cargo/reference/cargo-target.html#required-features)获取更多信息。
+该字段可以用于禁用特定的 Cargo Target:当某个 feature 没有被启用时,查看[这里](https://course.rs/toolchains/cargo/reference/cargo-target.html#required-features)获取更多信息。
## SemVer 兼容性
diff --git a/src/cargo/reference/intro.md b/src/toolchains/cargo/reference/intro.md
similarity index 100%
rename from src/cargo/reference/intro.md
rename to src/toolchains/cargo/reference/intro.md
diff --git a/src/cargo/reference/manifest.md b/src/toolchains/cargo/reference/manifest.md
similarity index 96%
rename from src/cargo/reference/manifest.md
rename to src/toolchains/cargo/reference/manifest.md
index 5dccea4b..ea413c1d 100644
--- a/src/cargo/reference/manifest.md
+++ b/src/toolchains/cargo/reference/manifest.md
@@ -261,11 +261,11 @@ workspace = "path/to/workspace/root"
- 该包是工作空间的根包(root crate),通过 `[workspace]` 指定)
- 该包是另一个工作空间的成员,通过 `package.workspace` 指定
-若要了解工作空间的更多信息,请参见[这里](https://course.rs/cargo/reference/workspaces.html)。
+若要了解工作空间的更多信息,请参见[这里](https://course.rs/toolchains/cargo/reference/workspaces.html)。
#### build
-`build` 用于指定位于项目根目录中的构建脚本,关于构建脚本的更多信息,可以阅读 [构建脚本](https://course.rs/cargo/reference/build-script/intro.html) 一章。
+`build` 用于指定位于项目根目录中的构建脚本,关于构建脚本的更多信息,可以阅读 [构建脚本](https://course.rs/toolchains/cargo/reference/build-script/intro.html) 一章。
```toml
[package]
@@ -277,7 +277,7 @@ build = "build.rs"
#### links
-用于指定项目链接的本地库的名称,更多的信息请看构建脚本章节的 [links](https://course.rs/cargo/reference/build-script/intro.html#links)
+用于指定项目链接的本地库的名称,更多的信息请看构建脚本章节的 [links](https://course.rs/toolchains/cargo/reference/build-script/intro.html#links)
```toml
[package]
@@ -390,9 +390,9 @@ maintenance = { status = "..." }
## [dependencies]
-在[之前章节](http://course.rs/cargo/reference/specify-deps.html)中,我们已经详细介绍过 `[dependencies]` 、 `[dev-dependencies]` 和 `[build-dependencies]`,这里就不再赘述。
+在[之前章节](http://course.rs/toolchains/cargo/reference/specify-deps.html)中,我们已经详细介绍过 `[dependencies]` 、 `[dev-dependencies]` 和 `[build-dependencies]`,这里就不再赘述。
## [profile.*]
-该部分可以对编译器进行配置,例如 debug 和优化,在后续的[编译器优化](http://course.rs/cargo/reference/profiles.html)章节有详细介绍。
+该部分可以对编译器进行配置,例如 debug 和优化,在后续的[编译器优化](http://course.rs/toolchains/cargo/reference/profiles.html)章节有详细介绍。
diff --git a/src/cargo/reference/package-id.md b/src/toolchains/cargo/reference/package-id.md
similarity index 100%
rename from src/cargo/reference/package-id.md
rename to src/toolchains/cargo/reference/package-id.md
diff --git a/src/cargo/reference/profile.md b/src/toolchains/cargo/reference/profile.md
similarity index 100%
rename from src/cargo/reference/profile.md
rename to src/toolchains/cargo/reference/profile.md
diff --git a/src/cargo/reference/profiles.md b/src/toolchains/cargo/reference/profiles.md
similarity index 97%
rename from src/cargo/reference/profiles.md
rename to src/toolchains/cargo/reference/profiles.md
index 9c407225..52c0681a 100644
--- a/src/cargo/reference/profiles.md
+++ b/src/toolchains/cargo/reference/profiles.md
@@ -49,7 +49,7 @@ lto = true
cargo build --profile release-lto
```
-与默认的 profile 相同,自定义 profile 的编译结果也存放在 [`target/`](https://course.rs/cargo/guide/build-cache.html) 下的同名目录中,例如 `--profile release-lto` 的输出结果存储在 `target/release-lto` 中。
+与默认的 profile 相同,自定义 profile 的编译结果也存放在 [`target/`](https://course.rs/toolchains/cargo/guide/build-cache.html) 下的同名目录中,例如 `--profile release-lto` 的输出结果存储在 `target/release-lto` 中。
## 选择 profile
@@ -257,7 +257,7 @@ codegen-units = 256
opt-level = 3
```
-这里的 `package` 名称实际上是一个 [`Package ID`](https://course.rs/cargo/reference/package-id.html),因此我们还可以通过版本号来选择: `[profile.dev.package."foo:2.1.0"]`。
+这里的 `package` 名称实际上是一个 [`Package ID`](https://course.rs/toolchains/cargo/reference/package-id.html),因此我们还可以通过版本号来选择: `[profile.dev.package."foo:2.1.0"]`。
如果要为所有依赖包重写(不包括工作空间的成员):
diff --git a/src/cargo/reference/publishing-on-crates.io.md b/src/toolchains/cargo/reference/publishing-on-crates.io.md
similarity index 84%
rename from src/cargo/reference/publishing-on-crates.io.md
rename to src/toolchains/cargo/reference/publishing-on-crates.io.md
index d10b4ce3..395767d2 100644
--- a/src/cargo/reference/publishing-on-crates.io.md
+++ b/src/toolchains/cargo/reference/publishing-on-crates.io.md
@@ -22,14 +22,14 @@ $ cargo login abcdefghijklmnopqrstuvwxyz012345
在发布之前,**确保** `Cargo.toml` 中以下字段已经被设置:
-- [license 或 license-file](https://course.rs/cargo/reference/manifest.html#license和license-file)
-- [description](https://course.rs/cargo/reference/manifest.html#description)
-- [homepage](https://course.rs/cargo/reference/manifest.html#homepage)
-- [documentation](https://course.rs/cargo/reference/manifest.html#documentation)
-- [repository](https://course.rs/cargo/reference/manifest.html#repository)
-- [readme](https://course.rs/cargo/reference/manifest.html#readme)
+- [license 或 license-file](https://course.rs/toolchains/cargo/reference/manifest.html#license和license-file)
+- [description](https://course.rs/toolchains/cargo/reference/manifest.html#description)
+- [homepage](https://course.rs/toolchains/cargo/reference/manifest.html#homepage)
+- [documentation](https://course.rs/toolchains/cargo/reference/manifest.html#documentation)
+- [repository](https://course.rs/toolchains/cargo/reference/manifest.html#repository)
+- [readme](https://course.rs/toolchains/cargo/reference/manifest.html#readme)
-你还可以设置[关键字](https://course.rs/cargo/reference/manifest.html#keywords)和[类别](https://course.rs/cargo/reference/manifest.html#categories)等元信息,让包更容易被其他人搜索发现,虽然它们不是必须的。
+你还可以设置[关键字](https://course.rs/toolchains/cargo/reference/manifest.html#keywords)和[类别](https://course.rs/toolchains/cargo/reference/manifest.html#categories)等元信息,让包更容易被其他人搜索发现,虽然它们不是必须的。
如果你发布的是一个依赖库,那么你可能需要遵循相关的[命名规范](https://course.rs/practice/naming.html)和 [API Guidlines](https://rust-lang.github.io/api-guidelines/).
@@ -55,7 +55,7 @@ $ cargo publish --dry-run
$cargo package --list
```
-当打包时,Cargo 会自动根据版本控制系统的配置来忽略指定的文件,例如 `.gitignore`。除此之外,你还可以通过 [`exclude`](https://course.rs/cargo/reference/manifest.html#exclude和include) 来排除指定的文件:
+当打包时,Cargo 会自动根据版本控制系统的配置来忽略指定的文件,例如 `.gitignore`。除此之外,你还可以通过 [`exclude`](https://course.rs/toolchains/cargo/reference/manifest.html#exclude和include) 来排除指定的文件:
```toml
[package]
@@ -91,7 +91,7 @@ $ cargo pulish
绝大多数时候,我们并不是在发布新包,而是发布已经上传过的包的新版本。
-为了实现这一点,只需修改 `Cargo.toml` 中的 [`version`](https://course.rs/cargo/reference/manifest.html#version) 字段 ,但需要注意:**版本号需要遵循 `semver` 规则**。
+为了实现这一点,只需修改 `Cargo.toml` 中的 [`version`](https://course.rs/toolchains/cargo/reference/manifest.html#version) 字段 ,但需要注意:**版本号需要遵循 `semver` 规则**。
然后再次使用 `cargo publish` 就可以上传新的版本了。
diff --git a/src/cargo/reference/specify-deps.md b/src/toolchains/cargo/reference/specify-deps.md
similarity index 97%
rename from src/cargo/reference/specify-deps.md
rename to src/toolchains/cargo/reference/specify-deps.md
index 5dc239a2..5197ee66 100644
--- a/src/cargo/reference/specify-deps.md
+++ b/src/toolchains/cargo/reference/specify-deps.md
@@ -145,7 +145,7 @@ regex = { git = "https://github.com/rust-lang/regex", branch = "next" }
**因此不要依赖锁定来完成版本的控制,而应该老老实实的在 `Cargo.toml` 小心配置你希望使用的版本。**
-如果访问的是私有仓库,你可能需要授权来访问该仓库,可以查看[这里](https://course.rs/cargo/git-auth.html)了解授权的方式。
+如果访问的是私有仓库,你可能需要授权来访问该仓库,可以查看[这里](https://course.rs/toolchains/cargo/git-auth.html)了解授权的方式。
#### 通过路径引入本地依赖包
@@ -223,7 +223,7 @@ openssl = "1.0.1"
如果你想要知道 `cfg` 能够作用的目标,可以在终端中运行 `rustc --print=cfg` 进行查询。当然,你可以指定平台查询: `rustc --print=cfg --target=x86_64-pc-windows-msvc`,该命令将对 `64bit` 的 Windows 进行查询。
-聪明的同学已经发现,这非常类似于条件依赖引入,那我们是不是可以根据自定义的条件来决定是否引入某个依赖呢?具体答案参见后续的 [feature](https://course.rs/cargo/reference/features.html) 章节。这里是一个简单的示例:
+聪明的同学已经发现,这非常类似于条件依赖引入,那我们是不是可以根据自定义的条件来决定是否引入某个依赖呢?具体答案参见后续的 [feature](https://course.rs/toolchains/cargo/reference/features.html) 章节。这里是一个简单的示例:
```toml
[dependencies]
@@ -310,7 +310,7 @@ default-features = false # 不要包含默认的 features,而是通过下面
features = ["secure-password", "civet"]
```
-更多的信息参见 [Features 章节](https://course.rs/cargo/reference/features.html)
+更多的信息参见 [Features 章节](https://course.rs/toolchains/cargo/reference/features.html)
## 在 Cargo.toml 中重命名依赖
diff --git a/src/cargo/reference/workspaces.md b/src/toolchains/cargo/reference/workspaces.md
similarity index 91%
rename from src/cargo/reference/workspaces.md
rename to src/toolchains/cargo/reference/workspaces.md
index e3d7b751..976798f0 100644
--- a/src/cargo/reference/workspaces.md
+++ b/src/toolchains/cargo/reference/workspaces.md
@@ -54,7 +54,7 @@ exclude = ["crates/proc_macro_test/imp"]
工作空间的几个关键点在于:
- 所有的 `package` 共享同一个 `Cargo.lock` 文件,该文件位于工作空间的根目录中
-- 所有的 `package` 共享同一个[输出目录](https://course.rs/cargo/guide/build-cache.html),该目录默认的名称是 `target` ,位于工作空间根目录下
+- 所有的 `package` 共享同一个[输出目录](https://course.rs/toolchains/cargo/guide/build-cache.html),该目录默认的名称是 `target` ,位于工作空间根目录下
- 只有工作空间根目录的 `Cargo.toml` 才能包含 `[patch]`, `[replace]` 和 `[profile.*]`,而成员的 `Cargo.toml` 中的相应部分将被自动忽略
## [workspace]
@@ -67,7 +67,7 @@ members = ["member1", "path/to/member2", "crates/*"]
exclude = ["crates/foo", "path/to/other"]
```
-若某个本地依赖包是通过 [`path`](https://course.rs/cargo/reference/specify-deps.html#通过路径引入本地依赖包) 引入,且该包位于工作空间的目录中,则该包自动成为工作空间的成员。
+若某个本地依赖包是通过 [`path`](https://course.rs/toolchains/cargo/reference/specify-deps.html#通过路径引入本地依赖包) 引入,且该包位于工作空间的目录中,则该包自动成为工作空间的成员。
剩余的成员需要通过 `workspace.members` 来指定,里面包含了各个成员所在的目录(成员目录中包含了 Cargo.toml )。
@@ -116,7 +116,7 @@ default-members = ["path/to/member2", "path/to/member3/foo"]
## workspace.metadata
-与 [package.metadata](https://course.rs/cargo/reference/manifest.html#metadata) 非常类似,`workspace.metadata` 会被 `Cargo` 自动忽略,就算没有被使用也不会发出警告。
+与 [package.metadata](https://course.rs/toolchains/cargo/reference/manifest.html#metadata) 非常类似,`workspace.metadata` 会被 `Cargo` 自动忽略,就算没有被使用也不会发出警告。
这个部分可以用于让工具在 `Cargo.toml` 中存储一些工作空间的配置元信息。例如:
diff --git a/src/toolchains/intro.md b/src/toolchains/intro.md
new file mode 100644
index 00000000..092c4695
--- /dev/null
+++ b/src/toolchains/intro.md
@@ -0,0 +1,5 @@
+# Rust 工具链指南
+
+工具链是一门语言的第二灵魂,如果问一个 C++ 程序员,他最羡慕 Rust 什么,那答案很可能就是 Rust 的工具链。
+
+而其中最主要的就是 Cargo 包管理工具,下面让我们从它开始。
\ No newline at end of file
diff --git a/内容变更记录.md b/内容变更记录.md
index c76d7d35..8fd108c2 100644
--- a/内容变更记录.md
+++ b/内容变更记录.md
@@ -1,8 +1,22 @@
# ChangeLog
记录一些值得注意的变更。
+## 2022-03-18
+
+- 新增章节: [不错的unsafe队列-基本操作](http://localhost:3000/too-many-lists/unsafe-queue/basics.html)
+- 新增章节: [不错的unsafe队列-使用miri](http://localhost:3000/too-many-lists/unsafe-queue/miri.html)
+
+## 2022-03-17
+
+- 将`原生指针`更名为`裸指针`
+- 新增章节: [不错的unsafe队列-数据布局](https://course.rs/too-many-lists/unsafe-queue/layout.html)
+- 新增章节: [deque-迭代器](https://course.rs/too-many-lists/deque/iterator.html)
+- 新增章节: [deque-最终代码](https://course.rs/too-many-lists/deque/final-code.html)
+
## 2022-03-16
+- 新增章节: [deque-基本操作的对称镜像](https://course.rs/too-many-lists/deque/symmetric.html)
+- 新增章节: [deque-Peek](https://course.rs/too-many-lists/deque/peek.html)
- 新增章节: [deque-数据布局和基本操作](https://course.rs/too-many-lists/deque/layout.html)
## 2022-03-14
@@ -55,15 +69,15 @@
## 2022-03-03
-- 新增章节: [Cargo - 构建脚本示例](https://course.rs/cargo/reference/build-script/examples.html)
+- 新增章节: [Cargo - 构建脚本示例](https://course.rs/toolchains/cargo/reference/build-script/examples.html)
## 2022-03-02
-- 新增章节: [Cargo - 构建脚本](https://course.rs/cargo/reference/build-script/intro.html)
+- 新增章节: [Cargo - 构建脚本](https://course.rs/toolchains/cargo/reference/build-script/intro.html)
## 2022-02-28
-- 新增章节: [Cargo - 发布到crates.io](https://course.rs/cargo/reference/publishing-on-crates.io.html)
+- 新增章节: [Cargo - 发布到crates.io](https://course.rs/toolchains/cargo/reference/publishing-on-crates.io.html)
- 新增内容:[结构体 - #[derive(Debug)]](https://course.rs/basic/compound-type/struct.html#使用-derivedebug-来打印结构体的信息)
## 2022-02-27
]