diff --git a/README.md b/README.md
index 6c9994ae..73e6f54b 100644
--- a/README.md
+++ b/README.md
@@ -1,52 +1,138 @@
-# Rust语言圣经 (The Course)
+
Rust语言圣经
-- 在线阅读
- - 官方: [https://course.rs](https://course.rs)
- - 知乎: [支持章节内目录跳转,很好用!](https://www.zhihu.com/column/c_1452781034895446017)
-
-- Rust语言社区: QQ群 1009730433
-
-> 学习 Rust 光看书不够,精心设计的习题和项目实践可以让你事半功倍。[Rust By Practice](https://github.com/sunface/rust-by-practice) 是本书的配套习题和实践,覆盖了 easy to hard 各个难度,满足大家对 Rust 的所有期待。
->
-> [Rust 语言周刊](https://github.com/sunface/rust-weekly),每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态。
->
-> Rust 优秀项目很多,如何在茫茫码海中与它们相遇?相比 Awesome Rust, [Fancy Rust](https://github.com/sunface/fancy-rust) 能带给你全新的体验和选择。
+
+

+
+
+
+
+[](https://github.com/studyrs) [](https://github.com/sunface/rust-course/stargazers)
+[](https://github.com/sunface/rust-course/issues)
+[](https://mybinder.org/v2/gh/ines/spacy-course/master)
+
+
+
+
+
+
+
+
## 教程简介
+- 在线阅读: https://course.rs
+
**`Rust语言圣经`**涵盖从**入门到精通**所需的 Rust 知识,目录及内容都经过深思熟虑的设计,同时语言生动幽默,行文流畅自如,摆脱技术书籍常有的机器味和晦涩感。
-在 Rust 基础教学的同时,我们还提供了(部分):
-
- **深入度**,在基础教学的同时,提供了深入剖析。浅尝辄止并不能让我们站上紫禁之巅
-- **性能优化**,选择 Rust,就意味着要追求性能,因此你需要体系化地了解性能优化
-- **专题**,将 Rust 高级内容通过专题的形式一一呈现,内容内聚性极强
-- **难点和错误索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
-- **场景化模版**,程序员上网查询如何操作文件是常事,没有人能记住所有代码,场景化模版可解君忧
-总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
+- **专题内容**,将 Rust 高级内容通过专题的形式一一呈现,内容内聚性极强,例如性能优化、手把手实现链表、Cargo和Tokio使用指南、async异步编程、标准库解析、WASM等等
-## ❤️ 开源
-本书是完全开源的,但是并不意味着质量上的妥协,这里的每一个章节都花费了大量的心血和时间才能完成,为此牺牲了陪伴家人、日常娱乐的时间,虽然我们并不后悔,但是如果能得到读者您的鼓励,我们将感激不尽。
+- **内容索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
-既然是开源,那最大的鼓励不是 money,而是 star:) **如果大家觉得这本书作者真的用心了,就帮我们点一个 🌟 吧,这将是我们继续前行最大的动力**
+- **规避陷阱和对抗编译器**,只有真的上手写过一长段时间 Rust 项目,才知道该如何规避常见的陷阱以及解决一些难搞的编译器错误,而本书将帮助你大大缩短这个过程,提前规避这些问题
-> 在开源版权上,我们选择了 [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 和阅读,但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,望理解。
+- **[Cookbook](https://rusty.rs)**,涵盖多个应用场景的实战代码片段,程序员上网查询文件操作、正则解析、数据库操作是常事,没有人能记住所有代码,而 Cookbook 可解君忧,Ctrl + C/V 走天下
+- **[配套练习题](https://github.com/sunface/rust-by-practice)**,像学习一门大学课程一样学习 Rust 是一种什么感觉?*Rust语言圣经 + Rust语言实战* 双剑合璧,给你最极致的学习体验
+总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
-## 贡献者
-
-非常感谢本教程的所有贡献者们,正是有了你们,才有了现在的高质量 Rust 教程!
-- [@AllanDowney](https://github.com/AllanDowney)
-- [@JesseAtSZ](https://github.com/JesseAtSZ)
-- [@1132719438](https://github.com/1132719438)
-- [@Mintnoii](https://github.com/Mintnoii)
-- [@Rustln](https://github.com/rustln)
+## 🏅 贡献者
+
+非常感谢本教程的[所有贡献者](https://github.com/sunface/rust-course/graphs/contributors),正是有了你们,才有了现在的高质量 Rust 教程!
+
+
+
+**🏆 贡献榜前三**(根据难易度、贡献次数、活跃度综合评定):
+
+
+
+
+🏅 核心贡献者:
+
+
+
+## 创作感悟
+
+截至目前,Rust语言圣经已写了 170 余章,110 余万字,历经 800 多个小时,每一个章节都是手动写就,没有任何机翻和质量上的妥协( 相信深入阅读过的读者都能体会到这一点 )。
+
+曾经有读者问过 "这么好的书为何要开源,而不是出版?",原因很简单:**只有完全开源才能完美地呈现出我想要的教学效果**。
+
+总之,Rust 要在国内真正发展起来,必须得有一些追逐梦想的人在做着不计付出的事情,而我希望自己能贡献一份微薄之力。
+
+但是要说完全无欲无求,那也是不可能的,看到项目多了一颗 🌟,那感觉...棒极了,因为它代表了读者的认可和称赞。
+
+你们用指尖绘制的星空,那里繁星点点,每一颗都在鼓励着怀揣着开源梦想的程序员披荆斩棘、不断前行,不夸张的说,没有你们,开源世界就没有星光,自然也就不会有今天的开源盛世。
+
+因此,**我恳请大家,如果觉得书还可以,就在你的指尖星空绘制一颗新的 🌟,指引我们继续砥砺前行**。这个人世间,因善意而美好。
+
+最后,能通过开源在茫茫人海中与大家相识,这感觉真好 :D
+
+
+## 开源协议
+
+在开源版权上,我们选择了 [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 和阅读,但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,望理解。
-尤其感谢这些主要贡献者,谢谢你们花费大量时间贡献了多处`fix`和高质量的内容优化。非常感动,再次感谢~~
## 借鉴的书籍
@@ -59,5 +145,14 @@
因为它们绝大部分是支持 APACHE + MIT 双协议的,因此我们选择了遵循其中的 MIT 协议,并在这里统一对借鉴的书籍进行说明。
-## Rust语言社区
-QQ群 1009730433, 欢迎大家加入,一起 happy,一起进步。
+
+## 社区 & 读者交流
+
+- 知乎: [孙飞 Sunface](https://www.zhihu.com/people/iSunface)
+
+- **StudyRust** 社区
+ - QQ群 `1009730433`,用于日常技术交流
+ - 微信公众号: 搜索 `studyrust` 或扫描下面的二维码关注公众号 `Rust语言中文网`
+
+
+
diff --git a/assets/Rust中英翻译对照表.md b/assets/Rust中英翻译对照表.md
deleted file mode 100644
index 111273f2..00000000
--- a/assets/Rust中英翻译对照表.md
+++ /dev/null
@@ -1,422 +0,0 @@
-> 词汇表是从https://github.com/rust-lang-cn/english-chinese-glossary-of-rust fork而来,原因是在部分词汇的翻译上,存在不同的意见,欢迎大家开issue讨论或者提交pr
-
-# Rust 语言术语中英文对照表
-
-English 英文 | Chinese 中文 | Note 备注
-------------------------------- |----------------------------- |----------
-**A** | |
-Abstract Syntax Tree | 抽象语法树 |
-ABI | 应用程序二进制接口 | Application Binary Interface 缩写
-accumulator | 累加器 |
-accumulator variable | 累加器变量 |
-ahead-of-time compiled | 预编译 |
-ahead-of-time compiled language | 预编译语言 |
-algebraic data types(ADT) | 代数数据类型 |
-alias | 别名 |
-aliasing | 别名使用 | 参见 [Wikipedia](https://en.wikipedia.org/wiki/Pointer_aliasing)
-angle brackets | 尖括号,“<”和“>” |
-annotate | 标注,注明(动词) |
-annotation | 标注,注明(名词) |
-ARC | 原子引用计数器 | Atomic Referecne Counter
-anonymity | 匿名 |
-argument | 参数,实参,实际参数 | 不严格区分的话, argument(参数)和
parameter(参量)可以互换地使用
-argument type | 参数类型 |
-assignment | 赋值 |
-associated functions | 关联函数 |
-associated items | 关联项 |
-associated types | 关联类型 |
-asterisk | 星号(\*) |
-atomic | 原子的 |
-attribute | 属性 |
-automated building | 自动构建 |
-automated test | 自动测试,自动化测试 |
-**B** | |
-baroque macro | 巴洛克宏 |
-benchmark | 基准 |
-binary | 二进制的 |
-binary executable | 二进制的可执行文件 |
-bind | 绑定 |
-block | 语句块,代码块 |
-boolean | 布尔型,布尔值 |
-borrow check | 借用检查 |
-borrower | 借用者,借入者 |
-borrowed | 借用的 |
-borrowing | 借用 |
-bound | 约束,限定,限制 | 此词和 constraint 意思相近,
constraint 在 C# 语言中翻译成“约束”
-box | 箱子,盒子,装箱类型 | 一般不译,作动词时翻译成“装箱”,
具有所有权的智能指针
-boxed | 装箱,装包 |
-boxing | 装箱,装包 |
-brace | 大括号,“{”或“}” |
-breaking changes | 破坏性变更 |
-buffer | 缓冲区 |
-build | 构建 |
-builder pattern | 创建者模式 |
-**C** | |
-call | 调用 |
-caller | 调用者 |
-capacity | 容量 |
-capture | 捕获 |
-cargo | (Rust 包管理器,不译) | 该词作名词时意思是“货物”,
作动词时意思是“装载货物”
-cargo-fy | Cargo 化,使用 Cargo 创建项目 |
-case analysis | 事例分析 |
-cast | 类型转换,转型 |
-casting | 类型转换 |
-chaining method call | 链式方法调用 |
-channel | 信道,通道 |
-closure | 闭包 |
-coercion | 强制类型转换,强制转换 | coercion 原意是“强制,胁迫”
-collection | 集合 | 参见 [Wikipedia](https://zh.wikipedia.org/wiki/%E9%9B%86%E5%90%88_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)) |
-combinator | 组合算子,组合器 |
-comma | 逗号,“,” |
-command | 命令 |
-command line | 命令行 |
-comment | 注释 |
-compile | 编译(动词) |
-compile time | 编译期,编译期间,编译时 |
-compilation | 编译(名词) |
-compilation unit | 编译单元 |
-compiler | 编译器 |
-compiler intrinsics | 编译器固有功能 |
-compound | 复合(类型,数据) |
-concurrency | 并发 |
-conditional compilation | 条件编译 |
-configuration | 配置 |
-constant | 常量 |
-constant raw pointer | 原生常量指针 |
-constructor | 构造器 |
-consumer | 消费者 |
-container | 容器 |
-container type | 容器类型 |
-convert | 转换,转化,转 |
-copy | 复制,拷贝 |
-crate | 包 | crate 是 Rust 的基本编译单元
-crate root | 包根 | 别拍我,我知道很奇葩
-curly braces | 大括号,包含“{”和“}” |
-custom type | 自定义类型 |
-**D** | |
-dangling pointer | 悬垂指针 | use after free 在释放后使用
-data race | 数据竞争 |
-dead code | 死代码,无效代码,不可达代码 |
-deallocate | 释放,重新分配 |
-declare | 声明 |
-deep copy | 深拷贝,深复制 |
-dependency | 依赖 |
-deref coercions | 解引用强制转换 |
-dereference | 解引用 | Rust 文章中有时简写为 Deref
-derive | 派生 |
-designator | 指示符 |
-destruction | 销毁,毁灭 |
-destructor | 析构器,析构函数 |
-destructure | 解构 |
-destructuring | 解构,解构赋值 |
-desugar | 脱糖 |
-diverge function | 发散函数 |
-device drive | 设备驱动 |
-directory | 目录 |
-dispatch | 分发 |
-diverging functions | 发散函数 |
-documentation | 文档 |
-dot operator | 点运算符 |
-DST | 动态大小类型 | dynamic sized type,一般不译,
使用英文缩写形式
-dynamic language | 动态类型语言 |
-dynamic trait type | 动态特质类型 |
-**E** | |
-enum variant | 枚举成员 |
-enumeration | 枚举 |
-encapsulation | 封装 |
-equality test | 相等测试 |
-elision | 省略 |
-exhaustiveness checking | 穷尽性检查,无遗漏检查 |
-executor | 执行器 |
-expression | 表达式 |
-expression-oriented language | 面向表达式的语言 |
-explicit | 显式 |
-explicit discriminator | 显式的辨别值 |
-explicit type conversion | 显式类型转换 |
-extension | 扩展名 |
-extern | 外,外部 | 作关键字时不译
-**F** | |
-fat pointer | 宽指针 |
-Feature | 暂时不译 | 在Rust中主要用于Cargo feature该词
-feature gate | 功能开关 |
-field | 字段 |
-field-level mutability | 字段级别可变性 |
-file | 文件 |
-fmt | 格式化,是 format 的缩写 |
-formatter | 格式化程序,格式化工具,格式器|
-floating-point number | 浮点数 |
-flow control | 流程控制 |
-Foreign Function Interface(FFI)| 外部语言函数接口 |
-fragment specifier | 片段分类符 |
-free variables | 自由变量 |
-freeze | 冻结 |
-function | 函数 |
-function declaration | 函数声明 |
-functional | 函数式 |
-**G** | |
-garbage collector | 垃圾回收 |
-generalize | 泛化,泛型化 |
-generator | 生成器 |
-generic | 泛型 |
-generic type | 泛型类型 |
-getter | 读访问器 |
-growable | 可增长的 |
-guard | 守卫 |
-**H** | |
-handle error | 句柄错误 |
-hash | 哈希,哈希值,散列 |
-hash map | 散列映射,哈希表 |
-heap | 堆 |
-hierarchy | 层次,分层,层次结构 |
-higher rank lifetime | 高阶生命周期 |
-higher rank trait bound | 高阶特质约束 |
-higher rank type | 高阶类型 |
-hygiene | 卫生 |
-hygienic macro system | 卫生宏系统 |
-**I** | |
-ICE | 编译内部错误 | internal compiler error 的缩写
-immutable | 不可变的 |
-implement | 实现 |
-implementor | 实现者 |
-implicit | 隐式 |
-implicit discriminator | 隐式的辨别值 |
-implicit type conversion | 隐式类型转换 |
-import | 导入 |
-in assignment | 在赋值(语句) |
-index | 索引 | 英语复数形式:indices
-infer | 推导(动词) |
-inference | 推导(名词) |
-inherited mutability | 承袭可变性 |
-inheritance | 继承 |
-integrated development
environment(IDE) | 集成开发环境 | 中文著作中通常直接写成 IDE
-integration-style test | 集成测试 |
-interior mutability | 内部可变性 |
-installer | 安装程序,安装器 |
-instance | 实例 |
-instance method | 实例方法 |
-integer | 整型,整数 |
-interact | 相互作用,相互影响 |
-interior mutability | 内部可变性 |
-intrinsic | 固有的 |
-invoke | 调用 |
-item | 项,条目,项目 |
-iterate | 重复 |
-iteration | 迭代 |
-iterator | 迭代器 |
-iterator adaptors | 迭代器适配器 |
-iterator invalidation | 迭代器失效 |
-**L** | |
-local variables | 局部变量 |
-LHS | 左操作数 | left-hand side 的非正式缩写,
与 RHS 相对
-lender | 借出者 |
-library | 库 |
-lifetime | 生命周期 |
-lifetime elision | 生命周期消除 |
-link | 链接 |
-linked-list | 链表 |
-lint | 代码静态分析 | Lint, or a linter, is a static code analysis tool used to flag programming errors, bugs, stylistic errors and suspicious constructs |
-list | 列表 |
-listener | 监听器 |
-literal | 数据,常量数据,字面值,字面量,
字面常量,字面上的 | 英文意思:字面意义的(内容)
-LLVM | (不译) | Low Level Virtual Machine 的缩写,
是构建编译器的系统
-loop | 循环 | 作关键字时不译
-low-level code | 底层代码 |
-low-level language | 底层语言 |
-l-value | 左值 |
-**M** | |
-main function | main 函数,主函数 |
-macro | 宏 |
-map | 映射 | 一般不译
-match guard | 匹配守卫 |
-memory | 内存 |
-memory leak | 内存泄露 |
-memory safe | 内存安全 |
-meta | 原则,元 |
-metadata | 元数据 |
-metaprogramming | 元编程 |
-metavariable | 元变量 |
-method call syntax | 方法调用语法 |
-method chaining | 方法链 |
-method definition | 方法定义 |
-modifier | 修饰符 |
-module | 模块 |
-monomorphization | 单态 | mono: one, morph: form
-move | 移动,转移 | 按照 Rust 所规定的内容,
英语单词 transfer 的意思
比 move 更贴合实际描述
参考:[Rust by Example](http://rustwiki.org/rust-by-example/scope/move.html)
-move semantics | 移动语义 |
-mutability | 可变性 |
-mutable | 可变 |
-mutable reference | 可变引用 |
-multiple bounds | 多重约束 |
-mutiple patterns | 多重模式 |
-**N** | |
-naming | 命名 |
-nest | 嵌套 |
-Nightly Rust | Rust 开发版 | nightly本意是“每夜,每天晚上”,
指代码每天都更新
-NLL | 非词法生命周期 | non lexical lifetime 的缩写,
一般不译
-non-copy type | 非复制类型 |
-non-generic | 非泛型 |
-no-op | 空操作,空运算 | (此词出现在类型转换章节中)
-non-commutative | 非交换的 |
-non-scalar cast | 非标量转换 |
-notation | 符号,记号 |
-number type | 数据类型
-numeric | 数值,数字 |
-**O** | |
-optimization | 优化 |
-out-of-bounds accessing | 越界访问 |
-orphan rule | 孤儿规则 |
-overflow | 溢出,越界 |
-own | 占有,拥有 |
-owned | 所拥有的 |
-owner | 所有者,拥有者 |
-ownership | 所有权 |
-**P** | |
-package | 不翻译 |
-panic | 异常、致命错误、不译 | 在 Rust 中用于不可恢复的错误处理,跟其它语言的exception类似
-parallelism | 并行 |
-parameter | 参数 |
-parametric polymorphism | 参数多态 |
-parent scope | 父级作用域 |
-parentheses | 小括号,包括“(”和“)” |
-parse | 分析,解析 |
-parser | (语法)分析器,解析器 |
-pattern | 模式 |
-pattern match | 模式匹配 |
-phantom type | 虚类型,虚位类型 | phantom 相关的专有名词:
phantom bug 幻影指令
phantom power 幻象电源
参见:[Haskell](https://wiki.haskell.org/Phantom_type)、[Haskell/Phantom_type](https://en.wikibooks.org/wiki/Haskell/Phantom_types)、
[Rust/Phantom](http://rustwiki.org/rust-by-example/generics/phantom.html)、[stdlib/PhantomData](https://doc.rust-lang.org/std/marker/struct.PhantomData.html)
-platform | 平台 |
-polymorphism | 多态 |
-powershell |(不译) | Windows 系统的一种命令行外壳程序
和脚本环境
-possibility of absence | 不存在的可能性 |
-precede | 预先?,在...发生(或出现) |
-prelude |(不译) | 预先导入模块,英文本意:序曲,前奏
-primitive types | 原生类型,基本类型,简单类型 |
-print | 打印 |
-process | 进程 |
-procedural macros | 过程宏,程序宏 |
-project | 项目,工程 |
-prototype | 原型 |
-**R** | |
-race condition | 竞态条件 |
-RAII | 资源获取即初始化(一般不译) | resource acquisition is initialization 的缩写
-range | 区间,范围 |
-range expression | 区间表达式 |
-raw identifier | 原生标识符 |
-raw pointer | 原生指针,裸指针 |
-RC | 引用计数 | reference counted
-reader | 读取器 |
-reader/writer | 读写器 |
-recursive macro | 递归宏 |
-reference | 引用 |
-reference cycle | 引用循环 |
-release | 发布 |
-resource | 资源 |
-resource leak | 资源泄露 |
-RHS | 右操作数 | right-hand side 的非正式缩写,
与 LHS 相对
-root directory | 根目录 |
-runtime | 运行时 |
-runtime behavior | 运行时行为 |
-runtime overhead | 运行时开销 |
-Rust | (不译) | 一种编程语言
-Rustacean | (不译) | 编写 Rust 的程序员或爱好者的通称
-rustc | (不译) | Rust 语言编译器
-r-value | 右值 |
-**S** | |
-scalar | 标量,数量 |
-schedule | 调度 |
-scope | 作用域 |
-screen | 屏幕 |
-script | 脚本 |
-semicolon | 分号,“;” |
-self | 自身,作关键字时不译 |
-setter | 写访问器 |
-shadow | 遮蔽,隐蔽,隐藏,覆盖 |
-shallow copy | 浅拷贝,浅复制 |
-signature | 标记 |
-slice | 切片 |
-snake case | 蛇形命名 | 参见:[Snake case](https://en.wikipedia.org/wiki/Snake_case)
-source file | 源文件 |
-source code | 源代码 |
-specialization | 泛型特化 |
-square | 平方,二次方,二次幂 |
-square brackets | 中括号,“[”和“]” |
-src | (不译) | source 的缩写,指源代码
-stack | 栈 |
-stack unwind | 栈解开、栈展开 |
-statement | 语句 |
-statically allocated | 静态分配 |
-statically allocated string | 静态分配的字符串 |
-statically dispatch | 静态分发 |
-static method | 静态方法 |
-string | 字符串 |
-string literal | 字符串常量 |
-string slices | 字符串切片 |
-stringify | 字符串化 |
-subscript notation | 下标 |
-sugar | 糖 |
-super | 父级,作关键字时不译 |
-syntax context | 语法上下文 |
-systems programming language | 系统级编程语言 |
-**T** | |
-tagged union | 标记联合 |
-target triple | 多层次指标,三层/重 指标/目标 | triple 本义是“三”,但此处虚指“多”,
此词翻译需要更多讨论
-terminal | 终端 |
-testing | 测试 |
-testsuit | 测试套件 |
-the least significant bit (LSB) | 最低数字位 |
-the most significant bit (MSB) | 最高数字位 |
-thread | 线程 |
-TOML | (不译) | Tom's Obvious, Minimal Language
的缩写,一种配置语言
-token tree | 令牌树? | 待进一步斟酌
-trait | 特征 | 其字面上有“特性,特征”之意
-trait bound | 特征约束 | bound 有“约束,限制,限定”之意
-trait object | 特征对象 |
-transmute | (不译) | 其字面上有“变化,变形,变异”之意,
不作翻译
-trivial | 平凡的 |
-troubleshooting | 疑难解答,故障诊断,
故障排除,故障分析 |
-tuple | 元组 |
-turbofish | 双冒号`::` | 难以翻译,所以直接用形译法
-two's complement | 补码,二补数 |
-two-word object | 双字对象 |
-type annotation | 类型标注 |
-type erasure | 类型擦除 |
-type inference | 类型推导 |
-type inference engine | 类型推导引擎 |
-type parameter | 类型参量 |
-type placeholder | 类型占位符 |
-type signature | 类型标记 |
-**U** | |
-undefined behavior | 未定义行为 |
-uninstall | 卸载 |
-unit-like struct | 类单元结构体 |
- unit struct | 单元结构体 |
-"unit-style" tests | 单元测试 |
-unit test | 单元测试 |
-unit type | 单元类型 |
-universal function call syntax
(UFCS) | 通用函数调用语法 |
-unsized types | 不定长类型 |
-unwind | 展开 |
-unwrap | 解包 | 暂译!
-**V** | |
-variable binding | 变量绑定 |
-variable shadowing | 变量遮蔽,变量隐蔽,
变量隐藏,变量覆盖 |
-variable capture | 变量捕获 |
-variant | 变量 |
-vector | (动态数组,一般不译) | vector 本义是“向量”
-visibility | 可见性 |
-vtable | 虚表 |
-**W** | |
-where clause | where 子句,where 从句,where 分句 | 在数据库的官方手册中多翻译成“子句”,英语语法中翻译成“从句”
-workspace | 工作空间 |
-wrap | 包装 | 暂译!
-wrapped | 装包 |
-wrapper | 装包 |
-writer | 写入器 |
-**Y** | |
-yield | 产生(收益、效益等),产出,提供|
-**Z** | |
-zero-cost abstractions | 零开销抽象 |
-zero-width space(ZWSP) | 零宽空格 |
-
-
-
-
diff --git a/assets/banner.jpg b/assets/banner.jpg
new file mode 100644
index 00000000..8f695097
Binary files /dev/null and b/assets/banner.jpg differ
diff --git a/assets/bigPicture.js b/assets/bigPicture.js
new file mode 100644
index 00000000..c5063bd6
--- /dev/null
+++ b/assets/bigPicture.js
@@ -0,0 +1 @@
+var BigPicture=function(){var t,n,e,o,i,r,a,c,p,s,l,d,u,f,m,b,g,h,x,v,y,w,_,T,k,M,S,L,E,A,H,z,I,C=[],D={},O="appendChild",N="createElement",V="removeChild";function W(){var n=t.getBoundingClientRect();return"transform:translate3D("+(n.left-(e.clientWidth-n.width)/2)+"px, "+(n.top-(e.clientHeight-n.height)/2)+"px, 0) scale3D("+t.clientWidth/o.clientWidth+", "+t.clientHeight/o.clientHeight+", 0)"}function q(t){var n=A.length-1;if(!u){if(t>0&&E===n||t<0&&!E){if(!I.loop)return j(i,""),void setTimeout(j,9,i,"animation:"+(t>0?"bpl":"bpf")+" .3s;transition:transform .35s");E=t>0?-1:n+1}if([(E=Math.max(0,Math.min(E+t,n)))-1,E,E+1].forEach(function(t){if(t=Math.max(0,Math.min(t,n)),!D[t]){var e=A[t].src,o=document[N]("IMG");o.addEventListener("load",F.bind(null,e)),o.src=e,D[t]=o}}),D[E].complete)return B(t);u=1,j(m,"opacity:.4;"),e[O](m),D[E].onload=function(){y&&B(t)},D[E].onerror=function(){A[E]={error:"Error loading image"},y&&B(t)}}}function B(n){u&&(e[V](m),u=0);var r=A[E];if(r.error)alert(r.error);else{var a=e.querySelector("img:last-of-type");j(i=o=D[E],"animation:"+(n>0?"bpfl":"bpfr")+" .35s;transition:transform .35s"),j(a,"animation:"+(n>0?"bpfol":"bpfor")+" .35s both"),e[O](i),r.el&&(t=r.el)}H.innerHTML=E+1+"/"+A.length,X(A[E].caption),M&&M([i,A[E]])}function P(){var t,n,e=.95*window.innerHeight,o=.95*window.innerWidth,i=I.dimensions||[1920,1080],r=i[0],a=i[1],p=a/r;p>e/o?n=(t=Math.min(a,e))/p:t=(n=Math.min(r,o))*p,c.style.cssText+="width:"+n+"px;height:"+t+"px;"}function G(t){~[1,4].indexOf(o.readyState)?(U(),setTimeout(function(){o.play()},99)):o.error?U(t):f=setTimeout(G,35,t)}function R(n){I.noLoader||(n&&j(m,"top:"+t.offsetTop+"px;left:"+t.offsetLeft+"px;height:"+t.clientHeight+"px;width:"+t.clientWidth+"px"),t.parentElement[n?O:V](m),u=n)}function X(t){t&&(g.innerHTML=t),j(b,"opacity:"+(t?"1;pointer-events:auto":"0"))}function F(t){!~C.indexOf(t)&&C.push(t)}function U(t){if(u&&R(),T&&T(),"string"==typeof t)return $(),I.onError?I.onError():alert("Error: The requested "+t+" could not be loaded.");_&&F(s),o.style.cssText+=W(),j(e,"opacity:1;pointer-events:auto"),k=setTimeout(k,410),v=1,y=!!A,setTimeout(function(){o.style.cssText+="transition:transform .35s;transform:none",h&&setTimeout(X,250,h)},60)}function Y(t){var n=t?t.target:e,i=[b,x,r,a,g,L,S,m];n.blur(),w||~i.indexOf(n)||(o.style.cssText+=W(),j(e,"pointer-events:auto"),setTimeout($,350),clearTimeout(k),v=0,w=1)}function $(){if((o===c?p:o).removeAttribute("src"),document.body[V](e),e[V](o),j(e,""),j(o,""),X(0),y){for(var t=e.querySelectorAll("img"),n=0;n',n}function d(t,n){var e=document[N]("button");return e.className="bp-lr",e.innerHTML='',j(e,n),e.onclick=function(n){n.stopPropagation(),q(t)},e}var f=document[N]("STYLE");f.innerHTML="#bp_caption,#bp_container{bottom:0;left:0;right:0;position:fixed;opacity:0}#bp_container>*,#bp_loader{position:absolute;right:0;z-index:10}#bp_container,#bp_caption,#bp_container svg{pointer-events:none}#bp_container{top:0;z-index:9999;background:rgba(0,0,0,.7);opacity:0;transition:opacity .35s}#bp_loader{top:0;left:0;bottom:0;display:flex;align-items:center;cursor:wait;background:0;z-index:9}#bp_loader svg{width:50%;max-width:300px;max-height:50%;margin:auto;animation:bpturn 1s infinite linear}#bp_aud,#bp_container img,#bp_sv,#bp_vid{user-select:none;max-height:96%;max-width:96%;top:0;bottom:0;left:0;margin:auto;box-shadow:0 0 3em rgba(0,0,0,.4);z-index:-1}#bp_sv{background:#111}#bp_sv svg{width:66px}#bp_caption{font-size:.9em;padding:1.3em;background:rgba(15,15,15,.94);color:#fff;text-align:center;transition:opacity .3s}#bp_aud{width:650px;top:calc(50% - 20px);bottom:auto;box-shadow:none}#bp_count{left:0;right:auto;padding:14px;color:rgba(255,255,255,.7);font-size:22px;cursor:default}#bp_container button{position:absolute;border:0;outline:0;background:0;cursor:pointer;transition:all .1s}#bp_container>.bp-x{padding:0;height:41px;width:41px;border-radius:100%;top:8px;right:14px;opacity:.8;line-height:1}#bp_container>.bp-x:focus,#bp_container>.bp-x:hover{background:rgba(255,255,255,.2)}.bp-x svg,.bp-xc svg{height:21px;width:20px;fill:#fff;vertical-align:top;}.bp-xc svg{width:16px}#bp_container .bp-xc{left:2%;bottom:100%;padding:9px 20px 7px;background:#d04444;border-radius:2px 2px 0 0;opacity:.85}#bp_container .bp-xc:focus,#bp_container .bp-xc:hover{opacity:1}.bp-lr{top:50%;top:calc(50% - 130px);padding:99px 0;width:6%;background:0;border:0;opacity:.4;transition:opacity .1s}.bp-lr:focus,.bp-lr:hover{opacity:.8}@keyframes bpf{50%{transform:translatex(15px)}100%{transform:none}}@keyframes bpl{50%{transform:translatex(-15px)}100%{transform:none}}@keyframes bpfl{0%{opacity:0;transform:translatex(70px)}100%{opacity:1;transform:none}}@keyframes bpfr{0%{opacity:0;transform:translatex(-70px)}100%{opacity:1;transform:none}}@keyframes bpfol{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(-70px)}}@keyframes bpfor{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(70px)}}@keyframes bpturn{0%{transform:none}100%{transform:rotate(360deg)}}@media (max-width:600px){.bp-lr{font-size:15vw}}",document.head[O](f),(e=document[N]("DIV")).id="bp_container",e.onclick=Y,l=s("bp-x"),e[O](l),"ontouchstart"in window&&(z=1,e.ontouchstart=function(n){var e=n.changedTouches;t=e[0].pageX},e.ontouchmove=function(t){t.preventDefault()},e.ontouchend=function(n){var e=n.changedTouches;if(y){var o=e[0].pageX-t;o<-30&&q(1),o>30&&q(-1)}}),i=document[N]("IMG"),(r=document[N]("VIDEO")).id="bp_vid",r.setAttribute("playsinline",1),r.controls=1,r.loop=1,(a=document[N]("audio")).id="bp_aud",a.controls=1,a.loop=1,(H=document[N]("span")).id="bp_count",(b=document[N]("DIV")).id="bp_caption",(x=s("bp-xc")).onclick=X.bind(null,0),b[O](x),g=document[N]("SPAN"),b[O](g),e[O](b),S=d(1,"transform:scalex(-1)"),L=d(-1,"left:0;right:auto"),(m=document[N]("DIV")).id="bp_loader",m.innerHTML='',(c=document[N]("DIV")).id="bp_sv",(p=document[N]("IFRAME")).setAttribute("allowfullscreen",1),p.allow="autoplay; fullscreen",p.onload=function(){return c[V](m)},j(p,"border:0;position:absolute;height:100%;width:100%;left:0;top:0"),c[O](p),i.onload=U,i.onerror=U.bind(null,"image"),window.addEventListener("resize",function(){y||u&&R(1),o===c&&P()}),document.addEventListener("keyup",function(t){var n=t.keyCode;27===n&&v&&Y(),y&&(39===n&&q(1),37===n&&q(-1),38===n&&q(10),40===n&&q(-10))}),document.addEventListener("keydown",function(t){y&&~[37,38,39,40].indexOf(t.keyCode)&&t.preventDefault()}),document.addEventListener("focus",function(t){v&&!e.contains(t.target)&&(t.stopPropagation(),l.focus())},1),n=1}(),u&&(clearTimeout(f),$()),I=w,d=w.ytSrc||w.vimeoSrc,T=w.animationStart,k=w.animationEnd,M=w.onChangeImage,_=0,h=(t=w.el).getAttribute("data-caption"),w.gallery?function(n,r){var a=I.galleryAttribute||"data-bp";if(Array.isArray(n))A=n,h=n[E=r||0].caption;else{var c=(A=[].slice.call("string"==typeof n?document.querySelectorAll(n+" ["+a+"]"):n)).indexOf(t);E=0===r||r?r:-1!==c?c:0,A=A.map(function(t){return{el:t,src:t.getAttribute(a),caption:t.getAttribute("data-caption")}})}_=1,!~C.indexOf(s=A[E].src)&&R(1),A.length>1?(e[O](H),H.innerHTML=E+1+"/"+A.length,z||(e[O](S),e[O](L))):A=0,(o=i).src=s}(w.gallery,w.position):d||w.iframeSrc?(o=c,I.ytSrc?W="https://www.youtube.com/embed/"+d+"?html5=1&rel=0&playsinline=1&autoplay=1":I.vimeoSrc?W="https://player.vimeo.com/video/"+d+"?autoplay=1":I.iframeSrc&&(W=I.iframeSrc),j(m,""),c[O](m),p.src=W,P(),setTimeout(U,9)):w.imgSrc?(_=1,!~C.indexOf(s=w.imgSrc)&&R(1),(o=i).src=s):w.audio?(R(1),(o=a).src=w.audio,G("audio file")):w.vidSrc?(R(1),w.dimensions&&j(r,"width:"+w.dimensions[0]+"px"),D=w.vidSrc,Array.isArray(D)?(o=r.cloneNode(),D.forEach(function(t){var n=document[N]("SOURCE");n.src=t,n.type="video/"+t.match(/.(\w+)$/)[1],o[O](n)})):(o=r).src=D,G("video")):(o=i).src="IMG"===t.tagName?t.src:window.getComputedStyle(t).backgroundImage.replace(/^url|[(|)|'|"]/g,""),e[O](o),document.body[O](e),{close:Y,next:function(){return q(1)},prev:function(){return q(-1)}};var W}}();
\ No newline at end of file
diff --git a/assets/custom.js b/assets/custom.js
new file mode 100644
index 00000000..59d19a51
--- /dev/null
+++ b/assets/custom.js
@@ -0,0 +1,158 @@
+var initAll = function () {
+ var path = window.location.pathname;
+ if (path.endsWith("/print.html")) {
+ return;
+ }
+
+ var images = document.querySelectorAll("main img")
+ Array.prototype.forEach.call(images, function (img) {
+ img.addEventListener("click", function () {
+ BigPicture({
+ el: img,
+ });
+ });
+ });
+
+ // Un-active everything when you click it
+ Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
+ el.addEventHandler("click", function () {
+ Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
+ el.classList.remove("active");
+ });
+ el.classList.add("active");
+ });
+ });
+
+ var updateFunction = function () {
+ var id = null;
+ var elements = document.getElementsByClassName("header");
+ Array.prototype.forEach.call(elements, function (el) {
+ if (window.pageYOffset >= el.offsetTop) {
+ id = el;
+ }
+ });
+
+ Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
+ el.classList.remove("active");
+ });
+
+ Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) {
+ if (id == null) {
+ return;
+ }
+ if (id.href.localeCompare(el.href) == 0) {
+ el.classList.add("active");
+ }
+ });
+ };
+
+ var pagetoc = document.getElementsByClassName("pagetoc")[0];
+ var elements = document.getElementsByClassName("header");
+ Array.prototype.forEach.call(elements, function (el) {
+ var link = document.createElement("a");
+
+ // Indent shows hierarchy
+ var indent = "";
+ switch (el.parentElement.tagName) {
+ case "H1":
+ return;
+ case "H3":
+ indent = "20px";
+ break;
+ case "H4":
+ indent = "40px";
+ break;
+ default:
+ break;
+ }
+
+ link.appendChild(document.createTextNode(el.text));
+ link.style.paddingLeft = indent;
+ link.href = el.href;
+ pagetoc.appendChild(link);
+ });
+ updateFunction.call();
+
+ // Handle active elements on scroll
+ window.addEventListener("scroll", updateFunction);
+
+ document.getElementById("theme-list").addEventListener("click", function (e) {
+ var iframe = document.querySelector('.giscus-frame');
+ if (!iframe) return;
+ var theme;
+ if (e.target.className === "theme") {
+ theme = e.target.id;
+ } else {
+ return;
+ }
+
+ // 若当前 mdbook 主题不是 Light 或 Rust ,则将 giscuz 主题设置为 transparent_dark
+ var giscusTheme = "light"
+ if (theme != "light" && theme != "rust") {
+ giscusTheme = "transparent_dark";
+ }
+
+ var msg = {
+ setConfig: {
+ theme: giscusTheme
+ }
+ };
+ iframe.contentWindow.postMessage({ giscus: msg }, 'https://giscus.app');
+ });
+
+ pagePath = pagePath.replace("index.md", "");
+ pagePath = pagePath.replace(".md", "");
+ if (pagePath.length > 0) {
+ if (pagePath.charAt(pagePath.length-1) == "/"){
+ pagePath = pagePath.substring(0, pagePath.length-1)
+ }
+ }else {
+ pagePath = "index"
+ }
+
+ // add vistors count
+ var ele = document.createElement("div");
+ ele.setAttribute("align","center");
+ var count = document.createElement("img")
+ count.setAttribute("src", "https://visitor-badge.glitch.me/badge?page_id=" + path);
+ ele.appendChild(count);
+ var divider =document.createElement("hr")
+
+ document.getElementById("giscus-container").appendChild(ele);
+ document.getElementById("giscus-container").appendChild(divider);
+
+ // 选取浏览器默认使用的语言
+ // const lang = navigator.language || navigator.userLanguage
+
+ // 若当前 mdbook 主题为 Light 或 Rust ,则将 giscuz 主题设置为 light
+ var theme = "transparent_dark";
+ const themeClass = document.getElementsByTagName("html")[0].className;
+ if (themeClass.indexOf("light") != -1 || themeClass.indexOf("rust") != -1) {
+ theme = "light"
+ }
+
+ var script = document.createElement("script")
+ script.type = "text/javascript";
+ script.src = "https://giscus.app/client.js";
+ script.async = true;
+ script.crossOrigin = "anonymous";
+ script.setAttribute("data-repo", "sunface/rust-course");
+ script.setAttribute("data-repo-id", "MDEwOlJlcG9zaXRvcnkxNDM4MjIwNjk=");
+ script.setAttribute("data-category", "章节评论区");
+ script.setAttribute("data-category-id", "DIC_kwDOCJKM9c4COQcP");
+ script.setAttribute("data-mapping", "specific");
+ script.setAttribute("data-term", pagePath);
+ script.setAttribute("data-reactions-enabled", "1");
+ script.setAttribute("data-emit-metadata", "0");
+ script.setAttribute("data-input-position", "top");
+ script.setAttribute("data-theme", theme);
+ // script.setAttribute("data-lang", lang);
+ // 预先加载评论会更好,这样用户读到那边时,评论就加载好了
+ // script.setAttribute("data-loading", "lazy");
+ document.getElementById("giscus-container").appendChild(script);
+
+
+
+};
+
+window.addEventListener('load', initAll);
\ No newline at end of file
diff --git a/assets/ferris.css b/assets/ferris.css
deleted file mode 100644
index b856d477..00000000
--- a/assets/ferris.css
+++ /dev/null
@@ -1,33 +0,0 @@
-body.light .does_not_compile,
-body.light .panics,
-body.light .not_desired_behavior,
-body.rust .does_not_compile,
-body.rust .panics,
-body.rust .not_desired_behavior {
- background: #fff1f1;
-}
-
-body.coal .does_not_compile,
-body.coal .panics,
-body.coal .not_desired_behavior,
-body.navy .does_not_compile,
-body.navy .panics,
-body.navy .not_desired_behavior,
-body.ayu .does_not_compile,
-body.ayu .panics,
-body.ayu .not_desired_behavior {
- background: #501f21;
-}
-
-.ferris {
- position: absolute;
- z-index: 99;
- right: 5px;
- top: 30px;
- width: 10%;
- height: auto;
-}
-
-.ferris-explain {
- width: 100px;
-}
\ No newline at end of file
diff --git a/assets/ferris.js b/assets/ferris.js
deleted file mode 100644
index 06a40cd7..00000000
--- a/assets/ferris.js
+++ /dev/null
@@ -1,51 +0,0 @@
-var ferrisTypes = [
- {
- attr: 'does_not_compile',
- title: 'This code does not compile!'
- },
- {
- attr: 'panics',
- title: 'This code panics!'
- },
- {
- attr: 'unsafe',
- title: 'This code block contains unsafe code.'
- },
- {
- attr: 'not_desired_behavior',
- title: 'This code does not produce the desired behavior.'
- }
-]
-
-document.addEventListener('DOMContentLoaded', () => {
- for (var ferrisType of ferrisTypes) {
- attachFerrises(ferrisType)
- }
-})
-
-function attachFerrises (type) {
- var elements = document.getElementsByClassName(type.attr)
-
- for (var codeBlock of elements) {
- var lines = codeBlock.textContent.split(/\r|\r\n|\n/).length - 1;
-
- if (lines >= 4) {
- attachFerris(codeBlock, type)
- }
- }
-}
-
-function attachFerris (element, type) {
- var a = document.createElement('a')
- a.setAttribute('href', 'ch00-00-introduction.html#ferris')
- a.setAttribute('target', '_blank')
-
- var img = document.createElement('img')
- img.setAttribute('src', '/img/ferris/' + type.attr + '.svg')
- img.setAttribute('title', type.title)
- img.className = 'ferris'
-
- a.appendChild(img)
-
- element.parentElement.insertBefore(a, element)
-}
\ No newline at end of file
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/assets/studyrust公众号.png b/assets/studyrust公众号.png
new file mode 100644
index 00000000..2fddd2d5
Binary files /dev/null and b/assets/studyrust公众号.png differ
diff --git a/assets/theme/2018-edition.css b/assets/theme/2018-edition.css
deleted file mode 100644
index b1dcf936..00000000
--- a/assets/theme/2018-edition.css
+++ /dev/null
@@ -1,9 +0,0 @@
-span.caption {
- font-size: .8em;
- font-weight: 600;
-}
-
-span.caption code {
- font-size: 0.875em;
- font-weight: 400;
-}
diff --git a/assets/zhihu.jpg b/assets/zhihu.jpg
new file mode 100644
index 00000000..a4075a25
Binary files /dev/null and b/assets/zhihu.jpg differ
diff --git a/book.toml b/book.toml
index 246e69a4..00c73b29 100644
--- a/book.toml
+++ b/book.toml
@@ -1,19 +1,19 @@
[book]
authors = ["sunface"]
language = "zh-CN"
-title = "Rust语言圣经(Rust教程 Rust Course)"
+title = "Rust语言圣经(Rust Course)"
src = "src"
[output.html]
-additional-css = ["assets/ferris.css", "assets/theme/2018-edition.css"]
-additional-js = ["assets/ferris.js"]
+additional-css = ["theme/style3.css"]
+additional-js = ["assets/custom.js", "assets/bigPicture.js"]
git-repository-url = "https://github.com/sunface/rust-course"
edit-url-template = "https://github.com/sunface/rust-course/edit/main/{path}"
[output.html.playground]
editable = true
copy-js = true
-line-numbers = true
+# line-numbers = true
[output.html.fold]
enable = true
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 800199b7..263b916f 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -1,22 +1,27 @@
# Rust 语言圣经
+[Rust语言圣经](about-book.md)
[进入 Rust 编程世界](into-rust.md)
-[AWS 为何这么喜欢 Rust?](usecases/aws-rust.md)
-[避免从入门到放弃](sth-you-should-not-do.md)
-[关于本书](about-book.md)
-
[快速查询入口](index-list.md)
-## Getting started
+---
+[StudyRust 社区](studyrust.md)
+[一本生锈的书](rusty-book.md)
+[Rust 语言周刊](rust-weekly.md)
+[Rustt 翻译计划](rustt.md)
+# 快速开始
+---
- [寻找牛刀,以便小试](first-try/intro.md)
- [安装 Rust 环境](first-try/installation.md)
- [墙推 VSCode!](first-try/editor.md)
- [认识 Cargo](first-try/cargo.md)
- [不仅仅是 Hello world](first-try/hello-world.md)
-
-## Rust 学习三部曲
-
+ - [下载依赖太慢了?](first-try/slowly-downloading.md)
+ - [避免从入门到放弃](first-try/sth-you-should-not-do.md)
+
+# Rust语言特性
+---
- [Rust 基础入门](basic/intro.md)
- [变量绑定与解构](basic/variable.md)
- [基本类型](basic/base-type/index.md)
@@ -58,6 +63,7 @@
- [使用 use 引入模块及受限可见性](basic/crate-module/use.md)
- [注释和文档](basic/comment.md)
- [格式化输出](basic/formatted-output.md)
+
- [Rust 高级进阶](advance/intro.md)
- [生命周期](advance/lifetime/intro.md)
- [认识生命周期](advance/lifetime/basic.md)
@@ -96,38 +102,47 @@
- [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)
-## 专题内容,每个专题都配套一个小型项目进行实践
-
+- [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)
+
+# 常用工具链
+---
- [自动化测试](test/intro.md)
- [编写测试及控制执行](test/write-tests.md)
- [单元测试和集成测试](test/unit-integration-test.md)
- [断言 assertion](test/assertion.md)
- - [用 Github Actions 进行持续集成](test/ci.md)
+ - [用 GitHub Actions 进行持续集成](test/ci.md)
- [基准测试 benchmark](test/benchmark.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)
@@ -151,9 +166,28 @@
- [通过 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)
+ - [构建脚本示例](cargo/reference/build-script/examples.md)
+
+# 开发实践
+---
+- [企业落地实践](usecases/intro.md)
+ - [AWS 为何这么喜欢 Rust?](usecases/aws-rust.md)
-- [手把手带你实现链表 doing](too-many-lists/intro.md)
+- [日志和监控](logs/intro.md)
+ - [日志详解](logs/about-log.md)
+ - [日志门面 log](logs/log.md)
+ - [使用 tracing 记录日志](logs/tracing.md)
+ - [自定义 tracing 的输出格式](logs/tracing-logger.md)
+ - [监控](logs/observe/intro.md)
+ - [可观测性](logs/observe/about-observe.md)
+ - [分布式追踪](logs/observe/trace.md)
+- [Rust 最佳实践](practice/intro.md)
+ - [日常开发三方库精选](practice/third-party-libs.md)
+ - [命名规范](practice/naming.md)
+ - [面试经验](practice/interview.md)
+ - [代码开发实践 todo](practice/best-pratice.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)
@@ -162,49 +196,61 @@
- [还可以的单向链表](too-many-lists/ok-stack/intro.md)
- [优化类型定义](too-many-lists/ok-stack/type-optimizing.md)
- [定义 Peek 函数](too-many-lists/ok-stack/peek.md)
+ - [IntoIter 和 Iter](too-many-lists/ok-stack/iter.md)
+ - [IterMut以及完整代码](too-many-lists/ok-stack/itermut.md)
+ - [持久化单向链表](too-many-lists/persistent-stack/intro.md)
+ - [数据布局和基本操作](too-many-lists/persistent-stack/layout.md)
+ - [Drop、Arc 及完整代码](too-many-lists/persistent-stack/drop-arc.md)
+ - [不咋样的双端队列](too-many-lists/deque/intro.md)
+ - [数据布局和基本操作](too-many-lists/deque/layout.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)
+ - [栈借用](too-many-lists/unsafe-queue/stacked-borrow.md)
+ - [测试栈借用](too-many-lists/unsafe-queue/testing-stacked-borrow.md)
+ - [数据布局2](too-many-lists/unsafe-queue/layout2.md)
+ - [额外的操作](too-many-lists/unsafe-queue/extra-junk.md)
+ - [最终代码](too-many-lists/unsafe-queue/final-code.md)
+ - [使用高级技巧实现链表](too-many-lists/advanced-lists/intro.md)
+ - [生产级可用的双向链表](too-many-lists/advanced-lists/unsafe-deque.md)
+ - [双单向链表](too-many-lists/advanced-lists/double-singly.md)
+ - [栈上的链表](too-many-lists/advanced-lists/stack-allocated.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)
+- [征服编译错误](compiler/intro.md)
+ - [对抗编译检查](compiler/fight-with-compiler/intro.md)
+ - [生命周期](compiler/fight-with-compiler/lifetime/intro.md)
+ - [生命周期过大-01](compiler/fight-with-compiler/lifetime/too-long1.md)
+ - [生命周期过大-02](compiler/fight-with-compiler/lifetime/too-long2.md)
+ - [循环中的生命周期](compiler/fight-with-compiler/lifetime/loop.md)
+ - [闭包碰到特征对象-01](compiler/fight-with-compiler/lifetime/closure-with-static.md)
+ - [重复借用](compiler/fight-with-compiler/borrowing/intro.md)
+ - [同时在函数内外使用引用](compiler/fight-with-compiler/borrowing/ref-exist-in-out-fn.md)
+ - [智能指针引起的重复借用错误](compiler/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.md)
+ - [类型未限制(todo)](compiler/fight-with-compiler/unconstrained.md)
+ - [幽灵数据(todo)](compiler/fight-with-compiler/phantom-data.md)
+ - [Rust 常见陷阱](compiler/pitfalls/index.md)
+ - [for 循环中使用外部数组](compiler/pitfalls/use-vec-in-for.md)
+ - [线程类型导致的栈溢出](compiler/pitfalls/stack-overflow.md)
+ - [算术溢出导致的 panic](compiler/pitfalls/arithmetic-overflow.md)
+ - [闭包中奇怪的生命周期](compiler/pitfalls/closure-with-lifetime.md)
+ - [可变变量不可变?](compiler/pitfalls/the-disabled-mutability.md)
+ - [可变借用失败引发的深入思考](compiler/pitfalls/multiple-mutable-references.md)
+ - [不太勤快的迭代器](compiler/pitfalls/lazy-iterators.md)
+ - [奇怪的序列 x..y](compiler/pitfalls/weird-ranges.md)
+ - [无处不在的迭代器](compiler/pitfalls/iterator-everywhere.md)
+ - [线程间传递消息导致主线程无法结束](compiler/pitfalls/main-with-channel-blocked.md)
+ - [警惕 UTF-8 引发的性能隐患](compiler/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)
+- [Rust 性能优化 todo](profiling/intro.md)
- [深入内存 todo](profiling/memory/intro.md)
- [指针和引用 todo](profiling/memory/pointer-ref.md)
- [未初始化内存 todo](profiling/memory/uninit.md)
@@ -236,28 +282,28 @@
- [HashMap todo](std/hashmap.md)
- [Iterator 常用方法 todo](std/iterator.md)
-- [Ctrl-C/V: 编程常用代码片段 todo](cases/intro.md)
- - [命令行解析 todo](cases/cmd.md)
- - [配置文件解析 todo](cases/config.md)
- - [编解码 todo](cases/encoding/intro.md)
- - [JSON](cases/encoding/json.md)
- - [CSV](cases/encoding/csv.md)
- - [protobuf](cases/encoding/protobuf.md)
- - [文件系统 todo](cases/file/intro.md)
- - [文件读写](cases/file/file.md)
- - [目录操作](cases/file/dir.md)
- - [网络通信 todo](cases/protocol/intro.md)
- - [HTTP](cases/protocol/http.md)
- - [TCP](cases/protocol/tcp.md)
- - [UDP](cases/protocol/udp.md)
- - [gRPC](cases/protocol/grpc.md)
- - [数据库访问 todo](cases/database.md)
- - [正则表达式 todo](cases/regexp.md)
- - [加密解密 todo](cases/crypto.md)
- - [时间日期](cases/date.md)
- - [开发调试 todo](cases/dev/intro.md)
- - [日志](cases/dev/logs.md)
- - [性能分析](cases/dev/profile.md)
+
+
+
-## 附录
-- [附录](appendix/intro.md)
- - [A-关键字](appendix/keywords.md)
- - [B-运算符与符号](appendix/operators.md)
- - [C-表达式](appendix/expressions.md)
- - [D-派生特征 trait](appendix/derive.md)
- - [E-prelude 模块 todo](appendix/prelude.md)
- - [F-Rust 版本说明](appendix/rust-version.md)
- - [G-Rust 更新版本列表](appendix/rust-versions/intro.md)
+# 附录
+---
+
+- [Appendix]()
+ - [关键字](appendix/keywords.md)
+ - [运算符与符号](appendix/operators.md)
+ - [表达式](appendix/expressions.md)
+ - [派生特征 trait](appendix/derive.md)
+ - [prelude 模块 todo](appendix/prelude.md)
+ - [Rust 版本说明](appendix/rust-version.md)
+ - [Rust 历次版本更新解读](appendix/rust-versions/intro.md)
- [1.58](appendix/rust-versions/1.58.md)
- [1.59](appendix/rust-versions/1.59.md)
+ - [1.60](appendix/rust-versions/1.60.md)
\ No newline at end of file
diff --git a/src/about-book.md b/src/about-book.md
index b2e9a377..5063e3b8 100644
--- a/src/about-book.md
+++ b/src/about-book.md
@@ -1,58 +1,106 @@
-# Rust 语言圣经 (The Course)
+
-- 在线阅读
- - 官方: [https://course.rs](https://course.rs)
- - 知乎: [支持章节内目录跳转,很好用!](https://www.zhihu.com/column/c_1452781034895446017)
+Rust语言真的好:连续六年成为全世界最受欢迎的语言、没有GC也无需手动内存管理、性能比肩 C++/C 还能直接调用它们的代码、安全性极高 - 总有公司说使用 Rust 后以前的大部分 bug 都将自动消失、全世界最好的包管理工具 Cargo 等等。但...
-> 学习 Rust 光看书不够,精心设计的习题和项目实践可以让你事半功倍。[Rust By Practice](https://github.com/sunface/rust-by-practice) 是本书的配套习题和实践,覆盖了 easy to hard 各个难度,满足大家对 Rust 的所有期待。
->
-> [Rust 语言周刊](https://github.com/sunface/rust-weekly),每周一发布,精选过去一周的技术文章、业界新闻、开源项目和 Rust 语言动态。
->
-> Rust 优秀项目很多,如何在茫茫码海中与它们相遇?相比 Awesome Rust, [Fancy Rust](https://github.com/sunface/fancy-rust) 能带给你全新的体验和选择。
-
-### 教程简介
-
-**`Rust语言圣经`**涵盖从**入门到精通**所需的 Rust 知识,目录及内容都经过深思熟虑的设计,同时语言生动幽默,行文流畅自如,摆脱技术书籍常有的机器味和晦涩感。
-
-在 Rust 基础教学的同时,我们还提供了(部分):
-
-- **深入度**,在基础教学的同时,提供了深入剖析。浅尝辄止并不能让我们站上紫禁之巅
-- **性能优化**,选择 Rust,意味着就要追求性能,因此你需要体系化的了解性能优化
-- **专题**,将 Rust 高级内容通过专题的方式一一呈现,内容内聚性极强
-- **难点和错误索引**,作为一本工具书,优秀的索引能力非常重要,遗忘不可怕,找不到才可怕
-- **场景化模版**,程序员上网查询如何操作文件是常事,没有人能记住所有代码,场景化模版可解君忧
-
-总之在写作过程中我们始终铭记初心:为中国用户打造一门**全面的、深入的、持续更新的** Rust 教程。 新手用来入门,老手用来提高,高手用来提升生产力。
-
-### 开源说明
-
-在开源版权上,我们选择了 [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 语言圣经是**完全开源**的电子书,每个章节都至少用时 4-6 个小时才能初步完稿,牺牲了大量休闲娱乐、陪伴家人的时间,还没有任何钱赚。
+对于后面一句话我们持保留意见,如果以找工作为标准,那国内环境确实还不好,但如果你想成为更优秀的程序员或者是玩转开源,那 Rust 还真是不错的选择,具体原因见[下一章](https://course.rs/into-rust.html)。
-**如果大家觉得这本书作者真的用心了,希望你能帮我们点一个 🌟 `star`。感激不尽!:)**
+至于 Rust 难学,那正是本书要解决的问题,如果看完后,你觉得没有学会 Rust,可以找我们退款,哦抱歉,这是开源书,那就退 🌟 吧:)
-### 借鉴的书籍
+如果看到这里,大家觉得这本书的介绍并没有吸引到你,不要立即放弃,强烈建议读一下[进入 Rust 编程世界](https://course.rs/into-rust.html),那里会有不一样的精彩。
-站在巨人的肩膀上,能帮我们看的更远,特此感谢以下巨人:
-
-- [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 协议,并在这里统一对借鉴的书籍进行说明。
-
-### 贡献者
-
-非常感谢本教程的所有贡献者们,正是有了你们,才有了现在的高质量 Rust 教程!
-
-- [@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)
-
-尤其感谢这些主要贡献者,谢谢你们花费大量时间贡献了多处`fix`和高质量的内容优化。非常感动,再次感谢~~
+> 本书完全开源,所有的文档内容都在 `GitHub` 上,至于里面还藏有什么秘密,大家点击右上角自行发现吧 :)
+>
+> 小秘密一: 你们可能会好奇,这本书到底与其它 Rust 书籍有[哪些不同](https://github.com/sunface/rust-course#教程简介)
+
+## 配套练习题
+对于学习编程而言,读一篇文章不如做几道练习题,此话虽然夸张,但是也不无道理。既然如此,即读书又做练习题,效果会不会更好?再加上练习题是书本的配套呢? :P
+
+- [Rust语言实战](https://github.com/sunface/rust-by-practice), Rust语言圣经配套习题,支持中英双语,可以在右上角切换
+
+## 创作感悟
+
+截至目前,Rust语言圣经已写了 170 余章,110 余万字,历经 800 多个小时,每一个章节都是手动写就,没有任何机翻和质量上的妥协( 相信深入阅读过的读者都能体会到这一点 )。
+
+曾经有读者问过 "这么好的书为何要开源,而不是出版?",原因很简单:**只有完全开源才能完美地呈现出我想要的教学效果**。
+
+总之,Rust 要在国内真正发展起来,必须得有一些追逐梦想的人在做着不计付出的事情,而我希望自己能贡献一份微薄之力。
+
+但是要说完全无欲无求,那也是不可能的,看到项目多了一颗 🌟,那感觉...棒极了,因为它代表了读者的认可和称赞。
+
+你们用指尖绘制的星空,那里繁星点点,每一颗都在鼓励着怀揣着开源梦想的程序员披荆斩棘、不断前行,不夸张的说,没有你们,开源世界就没有星光,自然也就不会有今天的开源盛世。
+
+因此,**我恳请大家,如果觉得书还可以,就在你的指尖星空绘制一颗新的 🌟,指引我们继续砥砺前行**。这个人世间,因善意而美好。
+
+最后,能通过开源在茫茫人海中与大家相识,这感觉真好 :D
+
+## 🏅 贡献者
+
+非常感谢本教程的[所有贡献者](https://github.com/sunface/rust-course/graphs/contributors),正是有了你们,才有了现在的高质量 Rust 教程!
+
+
+
+**🏆 贡献榜前三**(根据难易度、贡献次数、活跃度综合评定):
+
+
+
+
+🏅 核心贡献者:
+
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/concurrency-parallelism.md b/src/advance/concurrency-with-threads/concurrency-parallelism.md
index e8230106..8339c5d9 100644
--- a/src/advance/concurrency-with-threads/concurrency-parallelism.md
+++ b/src/advance/concurrency-with-threads/concurrency-parallelism.md
@@ -4,7 +4,7 @@
并行和并发其实并不难,但是也给一些用户造成了困扰,因此我们专门开辟一个章节,用于讲清楚这两者的区别。
-`Erlang` 之父 [`Joe Armstrong`]()(伟大的异步编程先驱,开创一个时代的殿堂级计算机科学家,我还犹记得当年刚学到 `Erlang` 时的震撼,respect!)用一张 5 岁小孩都能看到的图片解释了并发与并行的区别:
+`Erlang` 之父 [`Joe Armstrong`]()(伟大的异步编程先驱,开创一个时代的殿堂级计算机科学家,我还犹记得当年刚学到 `Erlang` 时的震撼,respect!)用一张 5 岁小孩都能看懂的图片解释了并发与并行的区别:
@@ -19,7 +19,7 @@
## CPU 多核
-现在的个人计算机动辄拥有十来个核心(M1 Max/Intel 12 代),如果使用串行的方式那真是太低调了,因此我们把各种任务简单分成多个队列,每个队列都交给一个 CPU 核心去执行,当某个 CPU 核心没有任务时,它还能去其它核心的队列中偷任务(真·老黄牛),这样就实现了并行化处理。
+现在的个人计算机动辄拥有十来个核心(M1 Max/Intel 12 代),如果使用串行的方式那真是太低效了,因此我们把各种任务简单分成多个队列,每个队列都交给一个 CPU 核心去执行,当某个 CPU 核心没有任务时,它还能去其它核心的队列中偷任务(真·老黄牛),这样就实现了并行化处理。
#### 单核心并发
diff --git a/src/advance/concurrency-with-threads/message-passing.md b/src/advance/concurrency-with-threads/message-passing.md
index d1aad470..dbf7ba23 100644
--- a/src/advance/concurrency-with-threads/message-passing.md
+++ b/src/advance/concurrency-with-threads/message-passing.md
@@ -226,7 +226,7 @@ fn main() {
thread::sleep(Duration::from_secs(3));
println!("睡眠之后");
- println!("收到值 {}", rx.recv().unwrap());
+ println!("receive {}", rx.recv().unwrap());
handle.join().unwrap();
}
```
@@ -239,7 +239,7 @@ fn main() {
发送之后
//···睡眠3秒
睡眠之后
-收到值 1
+receive 1
```
主线程因为睡眠阻塞了 3 秒,因此并没有进行消息接收,而子线程却在此期间轻松完成了消息的发送。等主线程睡眠结束后,才姗姗来迟的从通道中接收了子线程老早之前发送的消息。
@@ -279,11 +279,11 @@ fn main() {
发送之前
//···睡眠3秒
睡眠之后
-收到值 1
+receive 1
发送之后
```
-可以看出,主线程由于睡眠被阻塞导致无法接收消息,因此子线程的发送也一直被阻塞,直到主线程结束睡眠并成功接收消息后,发送才成功:**发送之后**的输出是在**收到值 1**之后,说明**只有接收消息彻底成功后,发送消息才算完成**。
+可以看出,主线程由于睡眠被阻塞导致无法接收消息,因此子线程的发送也一直被阻塞,直到主线程结束睡眠并成功接收消息后,发送才成功:**发送之后**的输出是在**receive 1**之后,说明**只有接收消息彻底成功后,发送消息才算完成**。
#### 消息缓存
diff --git a/src/advance/concurrency-with-threads/send-sync.md b/src/advance/concurrency-with-threads/send-sync.md
index 44dcdb27..df65953c 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`类型](https://course.rs/advance/into-types/custom-type.html#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..bd028963 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`来访问同一个资源.
@@ -99,7 +117,7 @@ fn main() {
}
```
-由于子线程需要通过`move`拿走锁的所有权,因此我们需要使用多所有权来保证每个线程都拿到数据的独立所有权,恰好智能指针[`Rc`](../smart-pointer/rc-arc.md)可以做到(**上面代码会报错**!具体往下看,别跳过-, -)。
+由于子线程需要通过`move`拿走锁的所有权,因此我们需要使用多所有权来保证每个线程都拿到数据的独立所有权,恰好智能指针[`Rc`](https://course.rs/advance/smart-pointer/rc-arc.html)可以做到(**上面代码会报错**!具体往下看,别跳过-, -)。
以上代码实现了在多线程中计数的功能,由于多个线程都需要去修改该计数器,因此我们需要使用锁来保证同一时间只有一个线程可以修改计数器,否则会导致脏数据:想象一下 A 线程和 B 线程同时拿到计数器,获取了当前值`1`, 并且同时对其进行了修改,最后值变成`2`,你会不会在风中凌乱?毕竟正确的值是`3`,因为两个线程各自加 1。
@@ -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>`
@@ -133,7 +151,7 @@ error[E0277]: `Rc>` cannot be sent between threads safely
##### 多线程安全的 Arc
-好在,我们有`Arc`,得益于它的[内部计数器](../smart-pointer/rc-arc.md#多线程无力的rc)是多线程安全的,因此可以在多线程环境中使用:
+好在,我们有`Arc`,得益于它的[内部计数器](https://course.rs/advance/smart-pointer/rc-arc.html#多线程无力的rc)是多线程安全的,因此可以在多线程环境中使用:
```rust
use std::sync::{Arc, Mutex};
@@ -169,7 +187,7 @@ Result: 10
#### 内部可变性
-在之前章节,我们提到过[内部可变性](../smart-pointer/cell-refcell.md#内部可变性),其中`Rc`和`RefCell`的结合,可以实现单线程的内部可变性。
+在之前章节,我们提到过[内部可变性](https://course.rs/advance/smart-pointer/cell-refcell.html#内部可变性),其中`Rc`和`RefCell`的结合,可以实现单线程的内部可变性。
现在我们又有了新的武器,由于`Mutex`可以支持修改内部数据,当结合`Arc`一起使用时,可以实现多线程的内部可变性。
@@ -186,7 +204,7 @@ Result: 10
正因为这种困难性,导致很多用户都热衷于使用消息传递的方式来实现同步,例如 Go 语言直接把`channel`内置在语言特性中,甚至还有无锁的语言,例如`erlang`,完全使用`Actor`模型,依赖消息传递来完成共享和同步。幸好 Rust 的类型系统、所有权机制、智能指针等可以很好的帮助我们减轻使用锁时的负担。
-另一个值的注意的是在使用`Mutex`时,Rust 无法帮我们避免所有的逻辑错误,例如在之前章节,我们提到过使用`Rc`可能会导致[循环引用的问题](../circle-self-ref/circle-reference.md)。类似的,`Mutex`也存在使用上的风险,例如创建死锁(deadlock):当一个操作试图锁住两个资源,然后两个线程各自获取其中一个锁,并试图获取另一个锁时,就会造成死锁。
+另一个值的注意的是在使用`Mutex`时,Rust 无法帮我们避免所有的逻辑错误,例如在之前章节,我们提到过使用`Rc`可能会导致[循环引用的问题](https://course.rs/advance/circle-self-ref/circle-reference.html)。类似的,`Mutex`也存在使用上的风险,例如创建死锁(deadlock):当一个操作试图锁住两个资源,然后两个线程各自获取其中一个锁,并试图获取另一个锁时,就会造成死锁。
## 死锁
@@ -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..c715b29b 100644
--- a/src/advance/concurrency-with-threads/sync2.md
+++ b/src/advance/concurrency-with-threads/sync2.md
@@ -6,7 +6,7 @@
由于原子操作是通过指令提供的支持,因此它的性能相比锁和消息传递会好很多。相比较于锁而言,原子类型不需要开发者处理加锁和释放锁的问题,同时支持修改,读取等操作,还具备较高的并发性能,几乎所有的语言都支持原子类型。
-可以看出原子类型是无锁类型,但是无锁不代表无需等待,因为原子类型内部使用了`CAS`循环,当大量的冲突发生时,该等待还是得[等待](./thread.md#多线程的开销)!但是总归比锁要好。
+可以看出原子类型是无锁类型,但是无锁不代表无需等待,因为原子类型内部使用了`CAS`循环,当大量的冲突发生时,该等待还是得[等待](https://course.rs/advance/concurrency-with-threads/thread.html#多线程的开销)!但是总归比锁要好。
> CAS 全称是 Compare and swap, 它通过一条指令读取指定的内存地址,然后判断其中的值是否等于给定的前置值,如果相等,则将其修改为新的值
@@ -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/advance/concurrency-with-threads/thread.md b/src/advance/concurrency-with-threads/thread.md
index c58fed54..b8c8910e 100644
--- a/src/advance/concurrency-with-threads/thread.md
+++ b/src/advance/concurrency-with-threads/thread.md
@@ -263,7 +263,7 @@ for handle in handles {
}
```
-按理来说,既然是无锁实现了,那么锁的开销应该几乎没有,性能会随着线程数的增加几近线程增长,但是真的是这样吗?
+按理来说,既然是无锁实现了,那么锁的开销应该几乎没有,性能会随着线程数的增加接近线性增长,但是真的是这样吗?
下图是该代码在 `48` 核机器上的运行结果:
@@ -475,10 +475,10 @@ fn main() {
```rust
use std::thread;
-use std::sync::{Once, ONCE_INIT};
+use std::sync::Once;
static mut VAL: usize = 0;
-static INIT: Once = ONCE_INIT;
+static INIT: Once = Once::new();
fn main() {
let handle1 = thread::spawn(move || {
@@ -516,7 +516,7 @@ fn main() {
## 总结
-[Rust 的线程模型](./intro.md)是 `1:1` 模型,因为 Rust 要保持尽量小的运行时。
+[Rust 的线程模型](https://course.rs/advance/concurrency-with-threads/intro.html)是 `1:1` 模型,因为 Rust 要保持尽量小的运行时。
我们可以使用 `thread::spawn` 来创建线程,创建出的多个线程之间并不存在执行顺序关系,因此代码逻辑千万不要依赖于线程间的执行顺序。
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 89%
rename from src/confonding/slice.md
rename to src/advance/confonding/slice.md
index 1abd097f..fab21e07 100644
--- a/src/confonding/slice.md
+++ b/src/advance/confonding/slice.md
@@ -24,7 +24,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t
| ^^^^^^ doesn't have a size known at compile-time
```
-编译器准确的告诉了我们原因:`str` 字符串切片它是 [`DST` 动态大小类型](https://course.rs/advance/custom-type.html#动态大小类型),这意味着编译器无法在编译期知道 `str` 类型的大小,只有到了运行期才能动态获知,这对于强类型、强安全的 Rust 语言来说是不可接受的。
+编译器准确的告诉了我们原因:`str` 字符串切片它是 [`DST` 动态大小类型](https://course.rs/advance/into-types/sized.html#动态大小类型-dst),这意味着编译器无法在编译期知道 `str` 类型的大小,只有到了运行期才能动态获知,这对于强类型、强安全的 Rust 语言来说是不可接受的。
也就是说,我们无法直接使用 `str`,而对于 `[u8]` 也是类似的,大家可以自己动手试试。
@@ -69,7 +69,7 @@ let s3: &[i32] = &arr[1..3];
我们常常说使用切片,实际上我们在用的是切片的引用,我们也在频繁说使用字符串,实际上我们在使用的也是字符串切片的引用。
-总之,切片在 Rust 中是动态类型 DST,是无法被我们直接使用的,而我们在使用的都是切片的引用。
+总之,切片在 Rust 中是动态大小类型 DST,是无法被我们直接使用的,而我们在使用的都是切片的引用。
| 切片 | 切片引用 |
| -------------- | --------------------- |
diff --git a/src/confonding/string.md b/src/advance/confonding/string.md
similarity index 80%
rename from src/confonding/string.md
rename to src/advance/confonding/string.md
index 640a1e95..9111e1fc 100644
--- a/src/confonding/string.md
+++ b/src/advance/confonding/string.md
@@ -8,7 +8,7 @@ Rust 语言的类型可以大致分为两种:基本类型和标准库类型,
## str
如上所述,`str` 是唯一定义在 Rust 语言特性中的字符串,但是也是我们几乎不会用到的字符串类型,为何?
-原因在于 `str` 字符串它是 [`DST` 动态大小类型](https://course.rs/advance/custom-type.html#动态大小类型),这意味着编译器无法在编译期知道 `str` 类型的大小,只有到了运行期才能动态获知,这对于强类型、强安全的 Rust 语言来说是不可接受的。
+原因在于 `str` 字符串它是 [`DST` 动态大小类型](https://course.rs/advance/into-types/sized.html#动态大小类型-dst),这意味着编译器无法在编译期知道 `str` 类型的大小,只有到了运行期才能动态获知,这对于强类型、强安全的 Rust 语言来说是不可接受的。
```rust
let string: str = "banana";
@@ -28,7 +28,7 @@ error[E0277]: the size for values of type `str` cannot be known at compilation t
同时还是 String 和 &str 的底层数据类型。 由于 str 是动态
-`str` 类型是硬编码进可执行文件,也无法被修改,但是 `String` 则是一个可增长、可改变且具有所有权的 UTF8 编码字符串,**当 Rust 用户提到字符串时,往往指的就是 `String` 类型和 `&str` 字符串切片类型,这两个类型都是 UTF8 编码**。
+`str` 类型是硬编码进可执行文件,也无法被修改,但是 `String` 则是一个可增长、可改变且具有所有权的 UTF-8 编码字符串,**当 Rust 用户提到字符串时,往往指的就是 `String` 类型和 `&str` 字符串切片类型,这两个类型都是 UTF-8 编码**。
除了 `String` 类型的字符串,Rust 的标准库还提供了其他类型的字符串,例如 `OsString`, `OsStr`, `CsString` 和` CsStr` 等,注意到这些名字都以 `String` 或者 `Str` 结尾了吗?它们分别对应的是具有所有权和被借用的变量。
diff --git a/src/advance/errors.md b/src/advance/errors.md
index 4d67e2d8..7716f104 100644
--- a/src/advance/errors.md
+++ b/src/advance/errors.md
@@ -513,7 +513,7 @@ fn render() -> Result {
上面的代码会报错,原因在于 `render` 函数中的两个 `?` 返回的实际上是不同的错误:`env::var()` 返回的是 `std::env::VarError`,而 `read_to_string` 返回的是 `std::io::Error`。
-为了满足 `render` 函数的签名,我们就需要将 `env::VarError` 和 `io::Error` 归一化为同一种错误类型。要实现这个目的有两种方式:
+为了满足 `render` 函数的签名,我们就需要将 `env::VarError` 和 `io::Error` 归一化为同一种错误类型。要实现这个目的有三种方式:
- 使用特征对象 `Box`
- 自定义错误类型
diff --git a/src/advance/functional-programing/closure.md b/src/advance/functional-programing/closure.md
index be2b6447..6dd2a4fc 100644
--- a/src/advance/functional-programing/closure.md
+++ b/src/advance/functional-programing/closure.md
@@ -220,7 +220,7 @@ let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
```
-可以看出第一行的函数和后面的闭包其实在形式上是非常接近的,同时三种不同的闭包也展示了三种不同的使用方式:省略参数、返回值和花括号对。
+可以看出第一行的函数和后面的闭包其实在形式上是非常接近的,同时三种不同的闭包也展示了三种不同的使用方式:省略参数、返回值类型和花括号对。
虽然类型推导很好用,但是它不是泛型,**当编译器推导出一种类型后,它就会一直使用该类型**:
@@ -303,7 +303,7 @@ where
}
```
-上面的缓存有一个很大的问题:只支持 `u32` 类型的值,若我们想要缓存 `String` 类型,显然就行不通了,因此需要将 `u32` 替换成泛型 `E`,该练习就留给读者自己完成,具体代码可以参考[这里](http://exercise.rs/functional-programming/closure.html)
+上面的缓存有一个很大的问题:只支持 `u32` 类型的值,若我们想要缓存 `String` 类型,显然就行不通了,因此需要将 `u32` 替换成泛型 `E`,该练习就留给读者自己完成,具体代码可以参考[这里](https://practice.rs/functional-programing/cloure.html#closure-in-structs)
## 捕获作用域中的值
@@ -421,6 +421,15 @@ false
如果你想强制闭包取得捕获变量的所有权,可以在参数列表前添加 `move` 关键字,这种用法通常用于闭包的生命周期大于捕获变量的生命周期时,例如将闭包返回或移入其他线程。
+```rust
+use std::thread;
+let v = vec![1, 2, 3];
+let handle = thread::spawn(move || {
+ println!("Here's a vector: {:?}", v);
+});
+handle.join().unwrap();
+```
+
2. `FnMut`,它以可变借用的方式捕获了环境中的值,因此可以修改该值:
```rust
@@ -699,7 +708,7 @@ help: use `impl Fn(i32) -> i32` as the return type, as all return paths are of t
嗯,编译器提示我们加一个 `impl` 关键字,哦,这样一说,读者可能就想起来了,`impl Trait` 可以用来返回一个实现了指定特征的类型,那么这里 `impl Fn(i32) -> i32` 的返回值形式,说明我们要返回一个闭包类型,它实现了 `Fn(i32) -> i32` 特征。
-完美解决,但是,在[特征](../../basic/trait/trait.md)那一章,我们提到过,`impl Trait` 的返回方式有一个非常大的局限,就是你只能返回同样的类型,例如:
+完美解决,但是,在[特征](https://course.rs/basic/trait/trait.html)那一章,我们提到过,`impl Trait` 的返回方式有一个非常大的局限,就是你只能返回同样的类型,例如:
```rust
fn factory(x:i32) -> impl Fn(i32) -> i32 {
@@ -758,3 +767,6 @@ fn factory(x:i32) -> Box i32> {
## 闭包的生命周期
这块儿内容在进阶生命周期章节中有讲,这里就不再赘述,读者可移步[此处](https://course.rs/advance/lifetime/advance.html#闭包函数的消除规则)进行回顾。
+
+
+{{#include ../../practice.md}}
\ No newline at end of file
diff --git a/src/advance/global-variable.md b/src/advance/global-variable.md
index 71a16e3a..05078bbd 100644
--- a/src/advance/global-variable.md
+++ b/src/advance/global-variable.md
@@ -71,7 +71,7 @@ fn main() {
}
```
-关于原子类型的讲解看[这篇文章](./concurrency-with-threads/sync2.md)
+关于原子类型的讲解看[这篇文章](https://course.rs/advance/concurrency-with-threads/sync2.html)
#### 示例:全局 ID 生成器
@@ -115,10 +115,10 @@ impl Factory{
```rust
use std::sync::Mutex;
-static names: Mutex = Mutex::new(String::from("Sunface, Jack, Allen"));
+static NAMES: Mutex = Mutex::new(String::from("Sunface, Jack, Allen"));
fn main() {
- let v = names.lock().unwrap();
+ let v = NAMES.lock().unwrap();
println!("{}",v);
}
```
@@ -129,10 +129,10 @@ fn main() {
error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
--> src/main.rs:3:42
|
-3 | static names: Mutex = Mutex::new(String::from("sunface"));
+3 | static NAMES: Mutex = Mutex::new(String::from("sunface"));
```
-但你又必须在声明时就对`names`进行初始化,此时就陷入了两难的境地。好在天无绝人之路,我们可以使用`lazy_static`包来解决这个问题。
+但你又必须在声明时就对`NAMES`进行初始化,此时就陷入了两难的境地。好在天无绝人之路,我们可以使用`lazy_static`包来解决这个问题。
#### lazy_static
@@ -142,11 +142,11 @@ error[E0015]: calls in statics are limited to constant functions, tuple structs
use std::sync::Mutex;
use lazy_static::lazy_static;
lazy_static! {
- static ref names: Mutex = Mutex::new(String::from("Sunface, Jack, Allen"));
+ static ref NAMES: Mutex = Mutex::new(String::from("Sunface, Jack, Allen"));
}
fn main() {
- let mut v = names.lock().unwrap();
+ let mut v = NAMES.lock().unwrap();
v.push_str(", Myth");
println!("{}",v);
}
@@ -195,27 +195,27 @@ struct Config {
a: String,
b: String,
}
-static mut config: Option<&mut Config> = None;
+static mut CONFIG: Option<&mut Config> = None;
fn main() {
unsafe {
- config = Some(&mut Config {
+ CONFIG = Some(&mut Config {
a: "A".to_string(),
b: "B".to_string(),
});
- println!("{:?}",config)
+ println!("{:?}", CONFIG)
}
}
```
-以上代码我们声明了一个全局动态配置`config`,并且其值初始化为`None`,然后在程序开始运行后,给它赋予相应的值,运行后报错:
+以上代码我们声明了一个全局动态配置`CONFIG`,并且其值初始化为`None`,然后在程序开始运行后,给它赋予相应的值,运行后报错:
```console
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:10:28
|
-10 | config = Some(&mut Config {
+10 | CONFIG = Some(&mut Config {
| _________-__________________^
| |_________|
| ||
@@ -228,19 +228,17 @@ error[E0716]: temporary value dropped while borrowed
| creates a temporary which is freed while still in use
```
-可以看到,Rust 的借用和生命周期规则限制了我们做到这一点,因为试图将一个局部生命周期的变量赋值给全局生命周期的`config`,这明显是不安全的。
+可以看到,Rust 的借用和生命周期规则限制了我们做到这一点,因为试图将一个局部生命周期的变量赋值给全局生命周期的`CONFIG`,这明显是不安全的。
-好在`Rust`为我们提供了`Box::leak`方法,它可以将一个变量从内存中泄漏(听上去怪怪的,竟然做主动内存泄漏),然后将其变为`'static`生命周期,最终该变量将和程序活得一样久,因此可以赋值给全局静态变量`config`。
+好在`Rust`为我们提供了`Box::leak`方法,它可以将一个变量从内存中泄漏(听上去怪怪的,竟然做主动内存泄漏),然后将其变为`'static`生命周期,最终该变量将和程序活得一样久,因此可以赋值给全局静态变量`CONFIG`。
```rust
-use std::sync::Mutex;
-
#[derive(Debug)]
struct Config {
a: String,
b: String
}
-static mut config: Option<&mut Config> = None;
+static mut CONFIG: Option<&mut Config> = None;
fn main() {
let c = Box::new(Config {
@@ -250,8 +248,8 @@ fn main() {
unsafe {
// 将`c`从内存中泄漏,变成`'static`生命周期
- config = Some(Box::leak(c));
- println!("{:?}", config);
+ CONFIG = Some(Box::leak(c));
+ println!("{:?}", CONFIG);
}
}
```
@@ -266,7 +264,7 @@ struct Config {
a: String,
b: String,
}
-static mut config: Option<&mut Config> = None;
+static mut CONFIG: Option<&mut Config> = None;
fn init() -> Option<&'static mut Config> {
Some(&mut Config {
@@ -278,9 +276,9 @@ fn init() -> Option<&'static mut Config> {
fn main() {
unsafe {
- config = init();
+ CONFIG = init();
- println!("{:?}",config)
+ println!("{:?}", CONFIG)
}
}
```
@@ -293,7 +291,7 @@ struct Config {
a: String,
b: String,
}
-static mut config: Option<&mut Config> = None;
+static mut CONFIG: Option<&mut Config> = None;
fn init() -> Option<&'static mut Config> {
let c = Box::new(Config {
@@ -307,16 +305,71 @@ fn init() -> Option<&'static mut Config> {
fn main() {
unsafe {
- config = init();
+ CONFIG = init();
- println!("{:?}",config)
+ println!("{:?}", CONFIG)
}
}
```
## 标准库中的 OnceCell
-@todo
+在 `Rust` 标准库中提供 `lazy::OnceCell` 和 `lazy::SyncOnceCell` 两种 `Cell`,前者用于单线程,后者用于多线程,它们用来存储堆上的信息,并且具有最多只能赋值一次的特性。 如实现一个多线程的日志组件 `Logger`:
+
+```rust
+#![feature(once_cell)]
+
+use std::{lazy::SyncOnceCell, thread};
+
+fn main() {
+ // 子线程中调用
+ let handle = thread::spawn(|| {
+ let logger = Logger::global();
+ logger.log("thread message".to_string());
+ });
+
+ // 主线程调用
+ let logger = Logger::global();
+ logger.log("some message".to_string());
+
+ let logger2 = Logger::global();
+ logger2.log("other message".to_string());
+
+ handle.join().unwrap();
+}
+
+#[derive(Debug)]
+struct Logger;
+
+static LOGGER: SyncOnceCell = SyncOnceCell::new();
+
+impl Logger {
+ fn global() -> &'static Logger {
+ // 获取或初始化 Logger
+ LOGGER.get_or_init(|| {
+ println!("Logger is being created..."); // 初始化打印
+ Logger
+ })
+ }
+
+ fn log(&self, message: String) {
+ println!("{}", message)
+ }
+}
+```
+
+以上代码我们声明了一个 `global()` 关联函数,并在其内部调用 `get_or_init` 进行初始化 `Logger`,之后在不同线程上多次调用 `Logger::global()` 获取其实例:
+
+```console
+Logger is being created...
+some message
+other message
+thread message
+```
+
+可以看到,`Logger is being created...` 在多个线程中使用也只被打印了一次。
+
+特别注意,目前 `OnceCell` 和 `SyncOnceCell` API 暂未稳定,需启用特性 `#![feature(once_cell)]`。
## 总结
diff --git a/src/advance/into-types/custom-type.md b/src/advance/into-types/custom-type.md
index 09700815..b837a1a8 100644
--- a/src/advance/into-types/custom-type.md
+++ b/src/advance/into-types/custom-type.md
@@ -110,7 +110,7 @@ type Meters = u32
**类型别名并不是一个独立的全新的类型,而是某一个类型的别名**,因此编译器依然会把 `Meters` 当 `u32` 来使用:
```rust
-type Meters = i32;
+type Meters = u32;
let x: u32 = 5;
let y: Meters = 5;
diff --git a/src/advance/into-types/enum-int.md b/src/advance/into-types/enum-int.md
index f9e924a8..e85fb69c 100644
--- a/src/advance/into-types/enum-int.md
+++ b/src/advance/into-types/enum-int.md
@@ -139,6 +139,7 @@ fn main() {
但是上面的代码有个问题,你需要为每个枚举成员都实现一个转换分支,非常麻烦。好在可以使用宏来简化,自动根据枚举的定义来实现`TryFrom`特征:
```rust
+#[macro_export]
macro_rules! back_to_enum {
($(#[$meta:meta])* $vis:vis enum $name:ident {
$($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)*
@@ -174,7 +175,7 @@ back_to_enum! {
**这个方法原则上并不推荐,但是有其存在的意义,如果要使用,你需要清晰的知道自己为什么使用**。
-在之前的类型转换章节,我们提到过非常邪恶的[`transmute`转换](<../basic/converse.md#变形记(Transmutes)>),其实,当你知道数值一定不会超过枚举的范围时(例如枚举成员对应 1,2,3,传入的整数也在这个范围内),就可以使用这个方法完成变形。
+在之前的类型转换章节,我们提到过非常邪恶的[`transmute`转换](https://course.rs/basic/converse.html#变形记transmutes),其实,当你知道数值一定不会超过枚举的范围时(例如枚举成员对应 1,2,3,传入的整数也在这个范围内),就可以使用这个方法完成变形。
> 最好使用#[repr(..)]来控制底层类型的大小,免得本来需要 i32,结果传入 i64,最终内存无法对齐,产生奇怪的结果
diff --git a/src/advance/into-types/sized.md b/src/advance/into-types/sized.md
index 9df9ee6e..b9d1fa4b 100644
--- a/src/advance/into-types/sized.md
+++ b/src/advance/into-types/sized.md
@@ -15,7 +15,7 @@
**正因为编译器无法在编译期获知类型大小,若你试图在代码中直接使用 DST 类型,将无法通过编译。**
-现在给你一个挑战:想出几个 DST 类型。俺厚黑地说一句,估计大部分人都想不出这样的一个类型,就连我,如果不是查询着资料在写,估计也一时半会儿想不到一个。
+现在给你一个挑战:想出几个 DST 类型。俺厚黑地说一句,估计大部分人都想不出这样的一个类型,就连我,如果不是查询着资料在写,估计一时半会儿也想不到一个。
先来看一个最直白的:
diff --git a/src/advance/lifetime/advance.md b/src/advance/lifetime/advance.md
index d00bb683..37f5e126 100644
--- a/src/advance/lifetime/advance.md
+++ b/src/advance/lifetime/advance.md
@@ -77,6 +77,8 @@ fn main() {
这就解释了可变借用为啥会在 `main` 函数作用域内有效,最终导致 `foo.share()` 无法再进行不可变借用。
+总结下:`&mut self` 借用的生命周期和 `loan` 的生命周期相同,将持续到 `println` 结束。而在此期间 `foo.share()` 又进行了一次不可变 `&foo` 借用,违背了可变借用与不可变借用不能同时存在的规则,最终导致了编译错误。
+
上述代码实际上完全是正确的,但是因为生命周期系统的“粗糙实现”,导致了编译错误,目前来说,遇到这种生命周期系统不够聪明导致的编译错误,我们也没有太好的办法,只能修改代码去满足它的需求,并期待以后它会更聪明。
#### 例子 2
@@ -136,7 +138,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 {
@@ -254,7 +256,7 @@ let closure_slision = |x: &i32| -> &i32 { x };
## NLL (Non-Lexical Lifetime)
-之前我们在[引用与借用](../../basic/ownership/borrowing.md#NLL)那一章其实有讲到过这个概念,简单来说就是:**引用的生命周期正常来说应该从借用开始一直持续到作用域结束**,但是这种规则会让多引用共存的情况变得更复杂:
+之前我们在[引用与借用](https://course.rs/basic/ownership/borrowing.html#NLL)那一章其实有讲到过这个概念,简单来说就是:**引用的生命周期正常来说应该从借用开始一直持续到作用域结束**,但是这种规则会让多引用共存的情况变得更复杂:
```rust
fn main() {
diff --git a/src/advance/lifetime/basic.md b/src/advance/lifetime/basic.md
index cc8da0a9..ffbd2ffd 100644
--- a/src/advance/lifetime/basic.md
+++ b/src/advance/lifetime/basic.md
@@ -44,7 +44,7 @@ error[E0597]: `x` does not live long enough // `x` 活得不够久
| - borrow later used here // 对 `x` 的借用在此处被使用
```
-在这里 `r` 拥有更大的作用域,或者说**活得更久**。如果 Rust 不阻止该垂悬引用的发生,那么当 `x` 被释放后,`r` 所引用的值就不再是合法的,会导致我们程序发生异常行为,且该异常行为有时候会很难被发现。
+在这里 `r` 拥有更大的作用域,或者说**活得更久**。如果 Rust 不阻止该悬垂引用的发生,那么当 `x` 被释放后,`r` 所引用的值就不再是合法的,会导致我们程序发生异常行为,且该异常行为有时候会很难被发现。
## 借用检查
@@ -578,7 +578,19 @@ Bang,一个复杂的玩意儿被甩到了你面前,就问怕不怕?
就关键点稍微解释下:
- `'a: 'b`,是生命周期约束语法,跟泛型约束非常相似,用于说明 `'a` 必须比 `'b` 活得久
-- 为了实现这一点,必须把 `'a` 和 `'b` 都在同一个地方声明,你不能把 `'a` 在 `impl` 后面声明,而把 `'b` 在方法中声明
+- 可以把 `'a` 和 `'b` 都在同一个地方声明(如上),或者分开声明但通过 `where 'a: 'b` 约束生命周期关系,如下:
+
+```rust
+impl<'a> ImportantExcerpt<'a> {
+ fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'b str
+ where
+ 'a: 'b,
+ {
+ println!("Attention please: {}", announcement);
+ self.part
+ }
+}
+```
总之,实现方法比想象中简单:加一个约束,就能暗示编译器,尽管引用吧,反正我想引用的内容比我活得久,爱咋咋地,我怎么都不会引用到无效的内容!
diff --git a/src/advance/lifetime/static.md b/src/advance/lifetime/static.md
index 89607cbb..737694c9 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);
}
```
@@ -80,7 +80,51 @@ fn main() {
## `T: 'static`
-相比起来,我们的生命周期约束就弱得多了,它只能试图向编译器表达:如果可以的话,我想要一个可以一直存活的变量, see ? 跟 `&'static` 表达的强度完全不一样,下面用例子来说明:
+相比起来,这种形式的约束就有些复杂了。
+
+首先,在以下两种情况下,`T: 'static` 与 `&'static` 有相同的约束:`T` 必须活得和程序一样久。
+
+```rust
+use std::fmt::Debug;
+
+fn print_it( input: T) {
+ println!( "'static value passed in is: {:?}", input );
+}
+
+fn print_it1( input: impl Debug + 'static ) {
+ println!( "'static value passed in is: {:?}", input );
+}
+
+
+
+fn main() {
+ let i = 5;
+
+ print_it(&i);
+ print_it1(&i);
+}
+```
+
+以上代码会报错,原因很简单: `&i` 的生命周期无法满足 `'static` 的约束,如果大家将 `i` 修改为常量,那自然一切 OK。
+
+见证奇迹的时候,请不要眨眼,现在我们来稍微修改下 `print_it` 函数:
+```rust
+use std::fmt::Debug;
+
+fn print_it( input: &T) {
+ println!( "'static value passed in is: {:?}", input );
+}
+
+fn main() {
+ let i = 5;
+
+ print_it(&i);
+}
+```
+
+这段代码竟然不报错了!原因在于我们约束的是 `T`,但是使用的却是它的引用 `&T`,换而言之,我们根本没有直接使用 `T`,因此编译器就没有去检查 `T` 的生命周期约束!它只要确保 `&T` 的生命周期符合规则即可,在上面代码中,它自然是符合的。
+
+再来看一个例子:
```rust
use std::fmt::Display;
@@ -121,49 +165,29 @@ fn static_bound(t: &T) {
}
```
-以上代码充分说明了两个问题:
-
-- `'static` 生命周期的数据可以一直存活,因此 `r1` 和 `r2` 才能在语句块内部被赋值
-- `T: 'static` 的约束真的很弱,`s1` 明明生命周期只在内部语句块内有效,但是该约束依然可以满足,`static_bound` 成功被调用
-
-## 两者的区别
-
-总之, `&'static` != `T: 'static` ,虽然它们看起来真的非常像。
-
-为了进一步验证,我们修改下 `static_bound` 的签名 :
+## static 到底针对谁?
+大家有没有想过,到底是 `&'static` 这个引用还是该引用指向的数据活得跟程序一样久呢?
+**答案是引用指向的数据**,而引用本身是要遵循其作用域范围的,我们来简单验证下:
```rust
-use std::fmt::Display;
-
fn main() {
- let s1 = "String".to_string();
+ {
+ let static_string = "I'm in read-only memory";
+ println!("static_string: {}", static_string);
- static_bound(&s1);
-}
+ // 当 `static_string` 超出作用域时,该引用不能再被使用,但是数据依然会存在于 binary 所占用的内存中
+ }
-fn static_bound(t: &'static T) {
- println!("{}", t);
+ println!("static_string reference remains alive: {}", static_string);
}
```
-在这里,不再使用生命周期约束来限制 `T`,而直接指定 `T` 的生命周期是 `&'static` ,不出所料,代码报错了:
-
-```console
-error[E0597]: `s1` does not live long enough
- --> src/main.rs:8:18
- |
-8 | static_bound(&s1);
- | -------------^^^-
- | | |
- | | borrowed value does not live long enough
- | argument requires that `s1` is borrowed for `'static`
-9 | }
- | - `s1` dropped here while still borrowed
-```
+以上代码不出所料会报错,原因在于虽然字符串字面量 "I'm in read-only memory" 的生命周期是 `'static`,但是持有它的引用并不是,它的作用域在内部花括号 `}` 处就结束了。
-原因很简单,`s1` 活得不够久,没有满足 `'static` 的生命周期要求。
-## 使用经验
+## 总结
+
+总之, `&'static` 和 `T: 'static` 大体上相似,相比起来,后者的使用形式会更加复杂一些。
至此,相信大家对于 `'static` 和 `T: 'static` 也有了清晰的理解,那么我们应该如何使用它们呢?
@@ -173,3 +197,4 @@ error[E0597]: `s1` does not live long enough
- 如果你希望满足和取悦编译器,那就使用 `T: 'static`,很多时候它都能解决问题
> 一个小知识,在 Rust 标准库中,有 48 处用到了 &'static ,112 处用到了 `T: 'static` ,看来取悦编译器不仅仅是菜鸟需要的,高手也经常用到 :)
+
diff --git a/src/advance/macro.md b/src/advance/macro.md
index e32678ef..f8bc3bca 100644
--- a/src/advance/macro.md
+++ b/src/advance/macro.md
@@ -311,7 +311,7 @@ hello_macro
由于过程宏所在的包跟我们的项目紧密相连,因此将它放在项目之中。现在,问题又来了,该如何在项目的 `src/main.rs` 中引用 `hello_macro_derive` 包的内容?
-方法有两种,第一种是将 `hello_macro_derive` 发布到 `crates.io` 或 `github` 中,就像我们引用的其它依赖一样;另一种就是使用相对路径引入的本地化方式,修改 `hello_macro/Cargo.toml` 文件添加以下内容:
+方法有两种,第一种是将 `hello_macro_derive` 发布到 `crates.io` 或 `GitHub` 中,就像我们引用的其它依赖一样;另一种就是使用相对路径引入的本地化方式,修改 `hello_macro/Cargo.toml` 文件添加以下内容:
```toml
[dependencies]
@@ -492,6 +492,7 @@ let sql = sql!(SELECT * FROM posts WHERE id=1);
3. [syn](https://crates.io/crates/syn) 和 [quote](https://crates.io/crates/quote) ,用于编写过程宏的包,它们的文档有很多值得学习的东西
4. [Structuring, testing and debugging procedural macro crates](https://www.reddit.com/r/rust/comments/rjumsg/any_good_resources_for_learning_rust_macros/),从测试、debug、结构化的角度来编写过程宏
5. [blog.turbo.fish](https://blog.turbo.fish),里面的过程宏系列文章值得一读
+6. [Rust 宏小册中文版](https://zjp-cn.github.io/tlborm/),非常详细的解释了宏各种知识
## 总结
diff --git a/src/advance/smart-pointer/box.md b/src/advance/smart-pointer/box.md
index 516380a4..8484182d 100644
--- a/src/advance/smart-pointer/box.md
+++ b/src/advance/smart-pointer/box.md
@@ -264,7 +264,7 @@ fn gen_static_str() -> &'static str{
光看上面的描述,大家可能还是云里雾里、一头雾水。
-那么我说一个简单的场景,**你需要一个在运行期初始化的值,但是可以全局有效,也就是和整个程序活得一样久**,那么久可以使用 `Box::leak`,例如有一个存储配置的结构体实例,它是在运行期动态插入内容,那么就可以将其转为全局有效,虽然 `Rc/Arc` 也可以实现此功能,但是 `Box::leak` 是性能最高的。
+那么我说一个简单的场景,**你需要一个在运行期初始化的值,但是可以全局有效,也就是和整个程序活得一样久**,那么就可以使用 `Box::leak`,例如有一个存储配置的结构体实例,它是在运行期动态插入内容,那么就可以将其转为全局有效,虽然 `Rc/Arc` 也可以实现此功能,但是 `Box::leak` 是性能最高的。
## 总结
diff --git a/src/advance/smart-pointer/drop.md b/src/advance/smart-pointer/drop.md
index 7413bc04..a80a9ac6 100644
--- a/src/advance/smart-pointer/drop.md
+++ b/src/advance/smart-pointer/drop.md
@@ -156,7 +156,7 @@ fn main() {
Bingo,完美拿走了所有权,而且这种实现保证了后续的使用必定会导致编译错误,因此非常安全!
-细心的同学可能已经注意到,这里直接调用了 `drop` 函数,并没有引入任何模块信息,原因是该函数在[`std::prelude`](../../appendix/prelude.md)里。
+细心的同学可能已经注意到,这里直接调用了 `drop` 函数,并没有引入任何模块信息,原因是该函数在[`std::prelude`](https://course.rs/appendix/prelude.html)里。
## Drop 使用场景
diff --git a/src/advance/smart-pointer/rc-arc.md b/src/advance/smart-pointer/rc-arc.md
index 30b394d1..4e414349 100644
--- a/src/advance/smart-pointer/rc-arc.md
+++ b/src/advance/smart-pointer/rc-arc.md
@@ -52,7 +52,7 @@ fn main() {
不要被 `clone` 字样所迷惑,以为所有的 `clone` 都是深拷贝。这里的 `clone` **仅仅复制了智能指针并增加了引用计数,并没有克隆底层数据**,因此 `a` 和 `b` 是共享了底层的字符串 `s`,这种**复制效率是非常高**的。当然你也可以使用 `a.clone()` 的方式来克隆,但是从可读性角度,我们更加推荐 `Rc::clone` 的方式。
-实际上在 Rust 中,还有不少 `clone` 都是浅拷贝,例如[迭代器的克隆](https://course.rs/pitfalls/iterator-everywhere.html)。
+实际上在 Rust 中,还有不少 `clone` 都是浅拷贝,例如[迭代器的克隆](https://course.rs/compiler/pitfalls/iterator-everywhere.html)。
#### 观察引用计数的变化
diff --git a/src/advance/unsafe/intro.md b/src/advance/unsafe/intro.md
index 28d5cd57..9a71f5fe 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..1d1c650c 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` 来告诉编译器,它无需操心,剩下的交给我们自己来处理。
@@ -407,7 +407,7 @@ extern "C" {
- 数据竞争
- 内存对齐问题
-但是需要注意的是,它只能帮助识别被执行代码路径的风险,哪些未被执行到的代码是没办法被识别的。
+但是需要注意的是,它只能帮助识别被执行代码路径的风险,那些未被执行到的代码是没办法被识别的。
#### Clippy
@@ -427,7 +427,7 @@ extern "C" {
## 总结
-至此,`unsafe` 的五种兵器已介绍完毕,大家是否意犹未尽?我想说的是,就算意犹未尽,也没有其它武器了。
+至此,`unsafe` 的五种兵器已介绍完毕,大家是否意犹未尽?我想说的是,就算意犹未尽,也没有其它兵器了。
就像上一章中所提到的,`unsafe` 只应该用于这五种场景,其它场景,你应该坚决的使用安全的代码,否则就会像 `actix-web` 的前作者一样,被很多人议论,甚至被喷。。。
diff --git a/src/appendix/derive.md b/src/appendix/derive.md
index 16260f05..0697ec52 100644
--- a/src/appendix/derive.md
+++ b/src/appendix/derive.md
@@ -16,7 +16,7 @@
一个无法被派生的特征例子是为终端用户处理格式化的 `Display` 。你应该时常考虑使用合适的方法来为终端用户显示一个类型。终端用户应该看到类型的什么部分?他们会找出相关部分吗?对他们来说最关心的数据格式是什么样的?Rust 编译器没有这样的洞察力,因此无法为你提供合适的默认行为。
-本附录所提供的可派生特征列表其实并不全面:库可以为其内部的特征实现 `derive` ,因此除了本文列出的标准库 `derive` 之外,还有很多很多其它库的 `derive` 。实现 `derive` 涉及到过程宏的应用,这在[宏章节](../advance/macro.md)中有介绍。
+本附录所提供的可派生特征列表其实并不全面:库可以为其内部的特征实现 `derive` ,因此除了本文列出的标准库 `derive` 之外,还有很多很多其它库的 `derive` 。实现 `derive` 涉及到过程宏的应用,这在[宏章节](https://course.rs/advance/macro.html)中有介绍。
### 用于开发者输出的 `Debug`
@@ -78,6 +78,6 @@
`Default` 特征会帮你创建一个类型的默认值。 派生 `Default` 意味着自动实现了 `default` 函数。 `default` 函数的派生实现调用了类型每部分的 `default` 函数,这意味着类型中所有的字段也必须实现了 `Default`,这样才能够派生 `Default` 。
-`Default::default` 函数通常结合结构体更新语法一起使用,这在第五章的 [结构体更新语法](../basic/compound-type/struct.md#结构体更新语法) 部分有讨论。可以自定义一个结构体的一小部分字段而剩余字段则使用 `..Default::default()` 设置为默认值。
+`Default::default` 函数通常结合结构体更新语法一起使用,这在第五章的 [结构体更新语法](https://course.rs/basic/compound-type/struct.html#结构体更新语法) 部分有讨论。可以自定义一个结构体的一小部分字段而剩余字段则使用 `..Default::default()` 设置为默认值。
例如,当你在 `Option` 实例上使用 `unwrap_or_default` 方法时, `Default` 特征是必须的。如果 `Option` 是 `None` 的话, `unwrap_or_default` 方法将返回 `T` 类型的 `Default::default` 的结果。
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/appendix/rust-versions/1.60.md b/src/appendix/rust-versions/1.60.md
new file mode 100644
index 00000000..846f0f6f
--- /dev/null
+++ b/src/appendix/rust-versions/1.60.md
@@ -0,0 +1,114 @@
+# Rust 新版解读 | 1.60 | 重点: 查看 Cargo 构建耗时详情、Cargo Feature 增加新语法
+
+> 原文链接: https://blog.rust-lang.org/2022/04/07/Rust-1.60.0.html
+
+
+通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.60 版本:
+```shell
+$ rustup update stable
+```
+
+## 基于源码的代码覆盖
+rustc 新增了基于 LLVM 的代码覆盖率测量,想要测试的同学可以通过以下方式重新构建你的项目:
+```shell
+RUSTFLAGS="-C instrument-coverage" cargo build
+```
+
+运行新生成的可执行文件将在当前目录下产生一个 `default.profraw` 文件( 路径和文件名可以通过环境变量进行[覆盖](https://doc.rust-lang.org/stable/rustc/instrument-coverage.html#running-the-instrumented-binary-to-generate-raw-coverage-profiling-data) )。
+
+`llvm-tools-preview` 组件包含了 `llvm-profdata`,可以用于处理和合并原生的测量结果输出(测量区域执行数)。
+
+`llvm-cov` 用于报告生成,它将 `llvm-profdata` 处理后的输出跟二进制可执行文件自身相结合,对于前者大家可能好理解,但是为何要跟后者可执行文件相结合呢?原因在于可执行文件中嵌入了一个从计数器到实际源代码单元的映射。
+
+```shell
+rustup component add llvm-tools-preview
+$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse default.profraw -o default.profdata
+$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show -Xdemangler=rustfilt target/debug/coverage-testing \
+ -instr-profile=default.profdata \
+ -show-line-counts-or-regions \
+ -show-instantiations
+```
+
+基于一个简单的 hello world 可执行文件,执行以上命令就可以获得如下带有标记的结果:
+```rust
+1| 1|fn main() {
+2| 1| println!("Hello, world!");
+3| 1|}
+```
+
+从结果中可以看出:每一行代码都已经被成功覆盖。
+
+如果大家还想要了解更多,可以看下[官方的 rustc 文档](https://doc.rust-lang.org/rustc/instrument-coverage.html)。目前来说,基准功能已经稳定了,并将以某种形式存在于未来所有的 Rust 发布版本中。 但输出格式和产生这些输出的 LLVM 工具可能依然会发生变化,基于此,大家在使用时需要确保 `llvm-tools-preview` 和 rustc ( 用于编译代码的 )使用了相同的版本。
+
+## 查看 Cargo 构建耗时
+新版本中,以下命令已经可以正常使用了:
+```shell
+$ cargo build --timings
+ Compiling hello-world v0.1.0 (hello-world)
+ Timing report saved to target/cargo-timings/cargo-timing-20220318T174818Z.html
+ Finished dev [unoptimized + debuginfo] target(s) in 0.98s
+```
+
+此命令会生成一个 `cargo build` 的耗时详情报告,除了上面提到的路径外,报告还会被拷贝到 `target/cargo-timings/cargo-timing.html`。这里是一个[在线示例](https://blog.rust-lang.org/images/2022-04-07-timing.html)。该报告在你需要提升构建速度时会非常有用,更多的信息请[查看文档](https://doc.rust-lang.org/nightly/cargo/reference/timings.html)。
+
+## Cargo Feature 的新语法
+
+> 关于 Cargo Features ,强烈推荐大家看看 [Cargo 使用指南](https://course.rs/cargo/reference/features/intro.html),可能是目前最好的中文翻译版本。
+
+新版本为 Cargo Features 引入了两个新的语法: 命名空间 ( Namespaced )和弱依赖,它们可以让 features 跟可选依赖进行更好的交互。
+
+Cargo 支持[可选依赖](https://course.rs/cargo/reference/features/intro.html#可选依赖)已经很久了,例如以下代码所示:
+```toml
+[dependencies]
+jpeg-decoder = { version = "0.1.20", default-features = false, optional = true }
+
+[features]
+# 通过开启 jpeg-decoder 依赖的 "rayon` feture,来启用并行化处理
+parallel = ["jpeg-decoder/rayon"]
+```
+
+这个例子有两点值得注意:
+
+- 可选依赖 `jpeg-decoder` 隐式地定义了一个同名的 feature,当启用 `jpeg-decoder` feature 时将同时启用 `jpeg-decoder`
+- `"jpeg-decoder/rayon"` 语法会启用 `jpeg-decoder` 依赖,并且还会启用 `jpeg-decoder` 依赖的 `rayon` feature
+
+而命名空间正是为了处理第一个问题而出现的。新版本中,我们可以在 `[features]` 中使用 `dep:` 前缀来显式地引用一个可选的依赖。再无需像第一点一样:先隐式的将可选依赖暴露为一个 feature,再通过 feature 来启用它。
+
+这样一来,我们将能更好的定义可选依赖所对应的 feture,包括将可选依赖隐藏在一个更具描述性的 feature 名称后面。
+
+弱依赖用于处理第二点: 根据第二点,`optional-dependency/feature-name` 必定会启用 `optional-dependency` 这个可选依赖。然而在一些场景中,我们只希望在其它 features 已经启用了可选依赖 `optional-dependency` 时才去启用 `feature-name` 这个 feature。
+
+从 1.60 开始,我们可以使用 `"package-name?/feature-name"` 这种带有 `?` 形式的语法: 只有当其它项已经启用了可选依赖 `package-name` 的情况下才去开启给定的 feature `feature-name`。
+
+> 译者注:简单来说,要启用 `feature` 必须需要别人先启用了其前置的可选依赖,再也无法像之前的第二点一样,既能开启可选依赖,又能启用 feature。
+
+例如,我们希望为自己的库增加一些序列化功能,它需要开启某个可选依赖中的指定 feature,可以这么做:
+```toml
+[dependencies]
+serde = { version = "1.0.133", optional = true }
+rgb = { version = "0.8.25", optional = true }
+
+[features]
+serde = ["dep:serde", "rgb?/serde"]
+```
+
+这里定义了以下关系:
+
+1. 开启 `serde` feature 将启用可选的 `serde` 依赖
+2. 只有当 `rgp` 依赖在其它地方已经被启用后,此处才能启用 `rgb` 的 `serde` feature
+
+## 增量编译重启开启
+在 [1.59 更新说明中](https://course.rs/appendix/rust-versions/1.59.html),我们有提到因为某些问题,增量编译被默认关闭了,现在官方修复了其中一些,并且确认目前的状态不会再影响用户的使用,因此在 1.60 版本中,增量编译又重新默认开启了。
+
+## Instant 单调性保证
+> 译者注:Instant 可以获取当前的时间,因此保证其单调增长是非常重要的,例如 uuid 的生成往往依赖于时间戳的单调增长,一旦时间回退,就可能出现 uuid 重复的情况。
+
+在目前所有的平台上,`Instant` 会去尝试使用系统提供的 API 来保证单调性行为( 目前主要针对 tier 1 的平台 )。然而在实际场景中,这种单调性偶尔会因为硬件、虚拟化或操作系统bug 等原因而失效。
+
+为了解决这些失效或是平台没有提供 API 的情况,`Instant::duration_since`, `Instant::elapsed` 和 `Instant::sub` 现在饱和为零( 这里不太好翻译,原文是 now saturate to zero,大概意思是非负?)。而在老版本中,这种时间回退的情况会导致 panic。
+
+`Instant::checked_duration_since` 也可以用于检测和处理单调性失败或 `Instants` 的减法顺序不正确的情况。
+
+但是目前的解决方法会遮掩一些错误的发生,因此在未来版本中,Rust 可能会重新就某些场景引入 panic 机制。
+
+在 1.60 版本前,单调性主要通过标准库的互斥锁 Mutex 或原子性 atomic 来保证,但是在 `Instant::now()` 调用频繁时,可能会导致明显的性能问题。
diff --git a/src/async/async-await.md b/src/async-rust/async/async-await.md
similarity index 98%
rename from src/async/async-await.md
rename to src/async-rust/async/async-await.md
index a0d953b7..4f039ddb 100644
--- a/src/async/async-await.md
+++ b/src/async-rust/async/async-await.md
@@ -22,7 +22,7 @@ fn bar() -> impl Future