Merge pull request #22 from sunface/main

sync
pull/1126/head
Rustln 3 years ago committed by GitHub
commit a3c1bbf2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -110,7 +110,7 @@ var initAll = function () {
pagePath = "index"
}
// add vistors count
// add visitors count
var ele = document.createElement("div");
ele.setAttribute("align","center");
var count = document.createElement("img")

@ -221,7 +221,7 @@
#### Features
* add Option2 exercise (#290) ([86b5c08b](https://github.com/rust-lang/rustlings/commit/86b5c08b9bea1576127a7c5f599f5752072c087d))
* add excercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4))
* add exercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4))
* add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229))
* **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71))

@ -341,4 +341,4 @@
- [1.59](appendix/rust-versions/1.59.md)
- [1.60](appendix/rust-versions/1.60.md)
- [1.61](appendix/rust-versions/1.61.md)
- [1.62](appendix/rust-versions/1.62.md)

@ -1,6 +1,6 @@
<img src="https://github.com/sunface/rust-course/blob/main/assets/banner.jpg?raw=true" />
Rust语言真的好连续年成为全世界最受欢迎的语言、没有GC也无需手动内存管理、性能比肩 C++/C 还能直接调用它们的代码、安全性极高 - 总有公司说使用 Rust 后以前的大部分 bug 都将自动消失、全世界最好的包管理工具 Cargo 等等。但...
Rust语言真的好连续年成为全世界最受欢迎的语言、没有GC也无需手动内存管理、性能比肩 C++/C 还能直接调用它们的代码、安全性极高 - 总有公司说使用 Rust 后以前的大部分 bug 都将自动消失、全世界最好的包管理工具 Cargo 等等。但...
**有人说: "Rust 太难了,学了也没用"。**

@ -102,7 +102,7 @@ fatal runtime error: stack overflow
以上的代码可能并不会造成什么大的问题,但是在一个更加复杂的程序中,类似的问题可能会造成你的程序不断地分配内存、泄漏内存,最终程序会不幸**OOM**,当然这其中的 CPU 损耗也不可小觑。
总之,创建引用并不简单,但是也并不是完全遇不到,当你使用 `RefCell<Rc<T>>` 或者类似的类型嵌套组合(具备内部可变性和引用计数)时,就要打起万分精神,前面可能是深渊!
总之,创建循环引用并不简单,但是也并不是完全遇不到,当你使用 `RefCell<Rc<T>>` 或者类似的类型嵌套组合(具备内部可变性和引用计数)时,就要打起万分精神,前面可能是深渊!
那么问题来了? 如果我们确实需要实现上面的功能,该怎么办?答案是使用 `Weak`

@ -191,7 +191,7 @@ Result: 10
现在我们又有了新的武器,由于`Mutex<T>`可以支持修改内部数据,当结合`Arc<T>`一起使用时,可以实现多线程的内部可变性。
简单总结下:`Rc<T>/RefCell<T>`用于单线程内部可变性, `Arc<T>/Mutext<T>`用于多线程内部可变性。
简单总结下:`Rc<T>/RefCell<T>`用于单线程内部可变性, `Arc<T>/Mutex<T>`用于多线程内部可变性。
#### 需要小心使用的 Mutex

@ -432,6 +432,7 @@ fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
```shell
$ cargo run
Running `target/debug/hello_macro`
Hello, Macro! My name is Sunfei!
Hello, Macro! My name is Sunface!

@ -83,7 +83,7 @@ fn main() {
事实上,`Rc<T>` 是指向底层数据的不可变的引用,因此你无法通过它来修改数据,这也符合 Rust 的借用规则:要么存在多个不可变借用,要么只能存在一个可变借用。
但是实际开发中我们往往需要对数据进行修改,这时单独使用 `Rc<T>` 无法满足我们的需求,需要配合其它数据类型来一起使用,例如内部可变性的 `RefCell<T>` 类型以及互斥锁 `Mutex<T>`。事实上,在多线程编程中,`Arc` 跟 `Mutext` 锁的组合使用非常常见,它们既可以让我们在不同的线程中共享数据,又允许在各个线程中对其进行修改。
但是实际开发中我们往往需要对数据进行修改,这时单独使用 `Rc<T>` 无法满足我们的需求,需要配合其它数据类型来一起使用,例如内部可变性的 `RefCell<T>` 类型以及互斥锁 `Mutex<T>`。事实上,在多线程编程中,`Arc` 跟 `Mutex` 锁的组合使用非常常见,它们既可以让我们在不同的线程中共享数据,又允许在各个线程中对其进行修改。
#### 一个综合例子

@ -36,7 +36,7 @@
| `->` | `fn(...) -> type`, <code>&vert;...&vert; -> type</code> | 函数与闭包,返回类型 | |
| `.` | `expr.ident` | 成员访问 | |
| `..` | `..`, `expr..`, `..expr`, `expr..expr` | 右半开区间 | PartialOrd |
| `..=` | `..`, `expr..`, `..expr`, `expr..expr` | 闭合区间 | PartialOrd |
| `..=` | `..=expr`, `expr..=expr` | 闭合区间 | PartialOrd |
| `..` | `..expr` | 结构体更新语法 | |
| `..` | `variant(x, ..)`, `struct_type { x, .. }` | “代表剩余部分”的模式绑定 | |
| `...` | `expr...expr` | (不推荐使用,用`..=`替代) 闭合区间 | |

@ -11,7 +11,7 @@ $ rustup update stable
## 基于源码的代码覆盖
rustc 新增了基于 LLVM 的代码覆盖率测量,想要测试的同学可以通过以下方式重新构建你的项目:
```shell
RUSTFLAGS="-C instrument-coverage" cargo build
$ 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) )。
@ -43,7 +43,7 @@ $(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show
## 查看 Cargo 构建耗时
新版本中,以下命令已经可以正常使用了:
```shell
$ cargo build --timings
$ 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

@ -0,0 +1,60 @@
# Rust 新版解读 | 1.62 | 重点: Cargo add#[default] 枚举变量Linux 上更薄更快的 Mutex裸机 x86_64 构架
> 原文链接: https://blog.rust-lang.org/2022/06/30/Rust-1.62.0.html
> 翻译 by [AllanDowney](https://github.com/AllanDowney)
通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.62 版本:
```shell
$ rustup update stable
```
## Cargo add
现在可以使用 `cargo add` 直接从命令行添加新的依赖项。此命令支持指定功能和版本。它还可以用来修改现有的依赖关系。
例如:
```rust
$ cargo add log
$ cargo add serde --features derive
$ cargo add nom@5
```
有关更多信息,请参阅 [cargo 文档](https://doc.rust-lang.org/nightly/cargo/commands/cargo-add.html)。
## `#[default]` 枚举变量
如果指定枚举默认变量,现在可以使用 `#[derive(Default)]`。例如,到目前为止,您必须手动为此枚举写入 `Default`
```rust
#[derive(Default)]
enum Maybe<T> {
#[default]
Nothing,
Something(T),
}
```
到目前为止,只允许将“单元”变量(没有字段的变量)标记为#[default]。[RFC](https://rust-lang.github.io/rfcs/3107-derive-default-enum.html) 中提供了有关此功能的更多信息。
## Linux 上更薄更快的 Mutex
以前Linux 上的 `pthreads` 库支持 `Mutex`、`Condvar` 和 `RwLock``pthreads 锁` 支持比 Rust API 本身更多的功能,包括运行时配置,并且设计用于比 Rust 提供的静态保证更少的语言中。
例如,`Mutex` 实现是 40 个字节,不能被移动(move)。这迫使标准库在后台为使用 `pthreads` 的平台的每个新 `Mutex` 分配一个 `Box`
现在 Rust 的标准库在 Linux 上提供了这些锁的原始 futex 实现,它非常轻量级,不需要额外分配。在 1.62.0 中,`Mutex` 在 Linux 上的内部状态只需要 5 个字节,尽管在未来的版本中可能会发生变化。
这是提高 Rust 的锁类型效率的长期努力的一部分,包括以前在 Windows 上的改进,如取消绑定其原语。您可以在[跟踪问题](https://github.com/rust-lang/rust/issues/93740)中了解更多有关这方面的信息。
## 裸机 `x86_64` 构架
现在更容易为 `x86_64` 构建无操作系统的二进制文件,例如在编写内核时。`x86_64-unknown-none` 构架已升级到第 2 层,可以用 `rustup` 安装。
```rust
$ rustup target add x86_64-unknown-none
$ rustc --target x86_64-unknown-none my_no_std_program.rs
```
您可以在 [Embedded Rust book](https://docs.rust-embedded.org/book/intro/no-std.html) 中阅读更多关于使用 `no_std` 进行开发的信息。

@ -45,6 +45,8 @@ async fn enjoy_book_and_music() -> (Book, Music) {
`Duang`,目标顺利达成。同时`join!`会返回一个元组,里面的值是对应的`Future`执行结束后输出的值。
> 如果希望同时运行一个数组里的多个异步任务,可以使用 `futures::future::join_all` 方法
## try_join!
由于`join!`必须等待它管理的所有 `Future` 完成后才能完成,如果你希望在某一个 `Future` 报错后就立即停止所有 `Future` 的执行,可以使用 `try_join!`,特别是当 `Future` 返回 `Result` 时:

@ -192,7 +192,7 @@ a: test1, b: test1
a: test2, b: test2
```
明知山有虎,偏向虎山行,这才是我辈年轻人的风华。既然移动数据会导致指针不合法,那我们就移动下数据试试,将 `test` 和 `test2` 进行下交换:
明知山有虎,偏向虎山行,这才是我辈年轻人的风华。既然移动数据会导致指针不合法,那我们就移动下数据试试,将 `test1` 和 `test2` 进行下交换:
```rust
fn main() {

@ -95,4 +95,4 @@ async fn some_operation(i: u64, _sender: Sender<()>) {
}
```
关于忘记 `drop` 本身持有的发送端进而导致 bug 的问题,大家可以看看[这篇文章](https://course.rs/pitfalls/main-with-channel-blocked.html)。
关于忘记 `drop` 本身持有的发送端进而导致 bug 的问题,大家可以看看[这篇文章](https://course.rs/compiler/pitfalls/main-with-channel-blocked.html)。

@ -19,10 +19,10 @@ Rust 使用一个相对传统的语法来创建整数(`1``2`...)和浮
| 16 位 | `i16` | `u16` |
| 32 位 | `i32` | `u32` |
| 64 位 | `i64` | `u64` |
| 128-位 | `i128` | `u128` |
| 128 位 | `i128` | `u128` |
| 视架构而定 | `isize` | `usize` |
类型定义的形式统一为:`有无符号 + 类型大小(位数)`。**无符号数**表示数字只能取正数,而**有符号**则表示数字可以取正数又可以取负数。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以[补码](https://en.wikipedia.org/wiki/Two%27s_complement)形式存储。
类型定义的形式统一为:`有无符号 + 类型大小(位数)`。**无符号数**表示数字只能取正数,而**有符号**则表示数字可以取正数又可以取负数。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以[补码](https://en.wikipedia.org/wiki/Two%27s_complement)形式存储。
每个有符号类型规定的数字范围是 -(2<sup>n - 1</sup>) ~ 2<sup>n -
1</sup> - 1其中 `n` 是该定义形式的位长度。因此 `i8` 可存储数字范围是 -(2<sup>7</sup>) ~ 2<sup>7</sup> - 1即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ 2<sup>n</sup> - 1所以 `u8` 能够存储的数字为 0 ~ 2<sup>8</sup> - 1即 0 ~ 255。
@ -39,7 +39,7 @@ Rust 使用一个相对传统的语法来创建整数(`1``2`...)和浮
| 二进制 | `0b1111_0000` |
| 字节 (仅限于 `u8`) | `b'A'` |
这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整默认使用 `i32`,例如 `let i = 1`,那 `i` 就是 `i32` 类型,因此你可以首选它,同时该类型也往往是性能最好的。`isize` 和 `usize` 的主要应用场景是用作集合的索引。
这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整默认使用 `i32`,例如 `let i = 1`,那 `i` 就是 `i32` 类型,因此你可以首选它,同时该类型也往往是性能最好的。`isize` 和 `usize` 的主要应用场景是用作集合的索引。
#### 整型溢出

@ -12,7 +12,7 @@
- 文档注释,支持 `Markdown`,对项目描述、公共 API 等用户关心的功能进行介绍,同时还能提供示例代码,目标读者往往是想要了解你项目的人
- 包和模块注释,严格来说这也是文档注释中的一种,它主要用于说明当前包和模块的功能,方便用户迅速了解一个项目
通过这些注释,实现了 Rust 极其优秀的文档化支持,甚至你还能在文档注释中写测试用例,省去了单独写测试用例的环节,我直好家伙!
通过这些注释,实现了 Rust 极其优秀的文档化支持,甚至你还能在文档注释中写测试用例,省去了单独写测试用例的环节,我直好家伙!
## 代码注释

@ -206,7 +206,7 @@ enum Websocket {
## Option 枚举用于处理空值
在其它编程语言中,往往都有一个 `null` 关键字,该关键字用于表明一个变量当前的值为空(不是零值,例如整的零值是 0也就是不存在值。当你对这些 `null` 进行操作时,例如调用一个方法,就会直接抛出**null 异常**,导致程序的崩溃,因此我们在编程时需要格外的小心去处理这些 `null` 空值。
在其它编程语言中,往往都有一个 `null` 关键字,该关键字用于表明一个变量当前的值为空(不是零值,例如整的零值是 0也就是不存在值。当你对这些 `null` 进行操作时,例如调用一个方法,就会直接抛出**null 异常**,导致程序的崩溃,因此我们在编程时需要格外的小心去处理这些 `null` 空值。
> Tony Hoare `null` 的发明者,曾经说过一段非常有名的话
>
@ -291,4 +291,4 @@ let none = plus_one(None);
## 课后练习
> [Rust By Practice](https://zh.practice.rs/compound-types/enum.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。
> [Rust By Practice](https://zh.practice.rs/compound-types/enum.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。

@ -99,7 +99,7 @@ let slice = &s[..];
字符串切片的类型标识是 `&str`,因此我们可以这样声明一个函数,输入 `String` 类型,返回它的切片: `fn first_word(s: &String) -> &str `
有了切片就可以写出这样的安全代码:
有了切片就可以写出这样的代码:
```rust
fn main() {

@ -304,7 +304,7 @@ fn main() {
// 以下全部都会补齐5个字符的长度
// 左对齐 => Hello x !
println!("Hello {:<5}!", "x");
// 右对齐 => Hello x
// 右对齐 => Hello x!
println!("Hello {:>5}!", "x");
// 居中对齐 => Hello x !
println!("Hello {:^5}!", "x");

@ -46,7 +46,7 @@ lto = true
然后在构建时使用 `--profile` 来指定想要选择的自定义 profile
```shell
cargo build --profile release-lto
$ cargo build --profile release-lto
```
与默认的 profile 相同,自定义 profile 的编译结果也存放在 [`target/`](https://course.rs/cargo/guide/build-cache.html) 下的同名目录中,例如 `--profile release-lto` 的输出结果存储在 `target/release-lto` 中。

@ -13,7 +13,7 @@ Rust 是一门全新的语言,它会带给你前所未有的体验,提升你
因此,在学习过程中,给大家三点建议:
- 要提前做好会遇到困难的准备,因为如上面所说,学习 Rust 不仅仅是在学习一门编程语言
- 不要抱着试一试的心态去试一试,否则是浪费时间和消耗学习的激情,作为连续年全世界最受喜欢的语言Rust 不仅仅是值得试一试 :)
- 不要抱着试一试的心态去试一试,否则是浪费时间和消耗学习的激情,作为连续年全世界最受喜欢的语言Rust 不仅仅是值得试一试 :)
- 深入学习一本好书或教程
总之Rust 入门难,但是在你一次次克服艰难险阻的同时,也一次次收获了与众不同的编程经验,最后历经九九八十一难,立地成大佬。 给自己一个机会,也给 Rust 一个机会

@ -70,28 +70,30 @@
[array 数组]: https://course.rs/basic/compound-type/array.html
[array slice]: https://course.rs/basic/compound-type/array.html#数组切片
[as转换]: https://course.rs/basic/converse.html#as转换
[as 转换]: https://course.rs/basic/converse.html#as转换
[back](#head)
## B
| 名称 | 关键字 | 简介 |
| ------------------ | ------------ | ------------------------------------------------------------------------------ |
| [变量遮蔽] | shadowing | 允许声明相同的变量名,后者会遮蔽掉前者 |
| [变量覆盖] | 模式匹配 | 无论是是 `match` 还是 `if let`,他们都可以在模式匹配时覆盖掉老的值,绑定新的值 |
| [变量作用域] | 所有权 | 作用域是一个变量在程序中有效的范围 |
| [表达式] | | 进行求值,结尾无 `;`,有返回值 |
| [bool 布尔] | 布尔类型 | `true` `false`,占用 1 字节 |
| [break] | 循环控制 | 直接跳出当前整个循环 |
| [backtrace 栈展开] | 不可恢复错误 | `RUST_BACKTRACE=1 cargo run` |
| | KWB | |
| 名称 | 关键字 | 简介 |
| ------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| [变量遮蔽] | shadowing | 允许声明相同的变量名,后者会遮蔽掉前者 |
| [变量覆盖] | 模式匹配 | 无论是是 `match` 还是 `if let`,他们都可以在模式匹配时覆盖掉老的值,绑定新的值 |
| [变量作用域] | 所有权 | 作用域是一个变量在程序中有效的范围 |
| [表达式]与[语句] | | 表达式:进行求值,结尾无 `;`,有返回值,如 `x + 9` 另见 [附录 C]</br>语句:完成一个操作,结尾有 `;` ,无返回值,如 `let x = 9;` |
| [bool 布尔] | 布尔类型 | `true` `false`,占用 1 字节 |
| [break] | 循环控制 | 直接跳出当前整个循环 |
| [backtrace 栈展开] | 不可恢复错误 | `RUST_BACKTRACE=1 cargo run` |
| | KWB | |
[变量遮蔽]: https://course.rs/basic/variable.html#变量遮蔽shadowing
[变量覆盖]: https://course.rs/basic/match-pattern/match-if-let.html#变量覆盖
[变量作用域]: https://course.rs/basic/ownership/ownership.html#变量作用域
[bool 布尔]: https://course.rs/basic/base-type/char-bool.html#布尔bool
[表达式]: https://course.rs/basic/base-type/statement-expression.html#表达式
[语句]: https://course.rs/basic/base-type/statement-expression.html#语句
[附录 c]: https://course.rs/appendix/expressions.html
[break]: https://course.rs/basic/flow-control.html#break
[backtrace 栈展开]: https://course.rs/basic/result-error/panic.html#backtrace-栈展开
@ -280,13 +282,15 @@
## N
| 名称 | 关键字 | 简介 |
| --------- | ------ | ------------------------------------------------------------------------------------------ |
| [newtype] | | 为一个[元组结构体]创建新类型。该元组结构体封装有一个字段,该字段就是希望实现特征的具体类型 |
| | KWN | |
| 名称 | 关键字 | 简介 |
| ------------------- | ------- | ------------------------------------------------------------------------------------------ |
| [newtype for Trait] | newtype | 为一个[元组结构体]创建新类型。该元组结构体封装有一个字段,该字段就是希望实现特征的具体类型 |
| [newtype ] | newtype | 深入 Rust 类型 |
| | KWN | |
[newtype]: https://course.rs/basic/trait/advance-trait.html#在外部类型上实现外部特征newtype
[newtype for trait]: https://course.rs/basic/trait/advance-trait.html#在外部类型上实现外部特征newtype
[元组结构体]: https://course.rs/basic/compound-type/struct.html#元组结构体tuple-struct
[newtype ]: http://localhost:8080/advance/into-types/custom-type.html#newtype
[back](#head)
@ -385,17 +389,17 @@
## T
| 名称 | 关键字 | 简介 |
| ------------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Tuple 元组] | | 由多种类型组合一起,元组的长度是固定的,元组中元素的顺序也是固定的<br>用模式匹配解构元组:`let (x, y, z) = (20, 19.2, 1)`<br>`.` 来访问元组:`tuple.0` 索引从 0 开始 |
| [Tuple Struct] | 元组结构体 | 结构体必须要有名称,但字段可以没有名称<br>`struct Color(i32, i32, i32);` |
| [Trait 特征] | 特征 | 一个可以被共享的行为,只要实现了特征,你就能使用该行为 |
| [T: Trait] | 特征约束 | 还可以有多重约束,`T: Trait1 + Trait2`<br>另见:[where 约束] |
| [Trait Object] | 特征对象 | 特征对象指向实现了 `Trait` 特征的类型的实例,可以在运行时通过特征对象找到具体调用的类型方法 |
| `type` 1. [关联类型] 2. [默认泛型类型参数] | | 1. `type Item;`<br>`Self` 用来指代当前调用者的具体类型,那么 `Self::em` 就用来指代该类型实现中定义的 `Item` 类型<br>2. `type Output = Struct;`<br>指定一个默认值,返回一个关联类型 `Output` |
| [特征定义中的特征约束] | 特征 | 用来说明一个特征需要实现另一个特征 |
| [TryInto 转换] | 类型转换 | 尝试进行一次转换,并返回一个 `Result`,可以对其进行相应的错误处理 |
| | KWT | |
| 名称 | 关键字 | 简介 |
| -------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Tuple 元组] | | 由多种类型组合一起,元组的长度是固定的,元组中元素的顺序也是固定的<br>用模式匹配解构元组:`let (x, y, z) = (20, 19.2, 1)`<br>`.` 来访问元组:`tuple.0` 索引从 0 开始 |
| [Tuple Struct] | 元组结构体 | 结构体必须要有名称,但字段可以没有名称<br>`struct Color(i32, i32, i32);` |
| [Trait 特征] | 特征 | 一个可以被共享的行为,只要实现了特征,你就能使用该行为 |
| [T: Trait] | 特征约束 | 还可以有多重约束,`T: Trait1 + Trait2`<br>另见:[where 约束] |
| [Trait Object] | 特征对象 | 特征对象指向实现了 `Trait` 特征的类型的实例,可以在运行时通过特征对象找到具体调用的类型方法 |
| `type` 1. [关联类型] 2. [默认泛型类型参数] 3. [类型别名] | | 1. `type Item;`<br>`Self` 用来指代当前调用者的具体类型,那么 `Self::em` 就用来指代该类型实现中定义的 `Item` 类型<br>2. `type Output = Struct;`<br>指定一个默认值,返回一个关联类型 `Output` |
| [特征定义中的特征约束] | 特征 | 用来说明一个特征需要实现另一个特征 |
| [TryInto 转换] | 类型转换 | 尝试进行一次转换,并返回一个 `Result`,可以对其进行相应的错误处理 |
| | KWT | |
[tuple 元组]: https://course.rs/basic/compound-type/tuple.html#元组
[tuple struct]: https://course.rs/basic/compound-type/struct.html#元组结构体tuple-struct
@ -406,6 +410,7 @@
[默认泛型类型参数]: https://course.rs/basic/trait/advance-trait.html#默认泛型类型参数
[特征定义中的特征约束]: https://course.rs/basic/trait/advance-trait.html#特征定义中的特征约束
[tryinto 转换]: https://course.rs/basic/converse.html#tryinto-转换
[类型别名]: https://course.rs/advance/into-types/custom-type.html#类型别名type-alias
[back](#head)
@ -469,12 +474,9 @@
## Y
| 名称 | 关键字 | 简介 |
| ------ | ------ | ---------------------------------------------------- |
| [语句] | | 完成一个操作,结尾有 `;` ,无返回值,如 `let x = 9;` |
| | KWY | |
[语句]: https://course.rs/basic/base-type/statement-expression.html#语句
| 名称 | 关键字 | 简介 |
| ---- | ------ | ---- |
| | KWY | |
[back](#head)

@ -262,7 +262,7 @@ fn main() {
tracing_subscriber::registry().with(fmt::layer()).init();
let s = span!(Level::TRACE, "my span");
// 没进入 span因此输出日志将不带上 span 的信息
// 没进入 span因此输出日志将不带上 span 的信息
event!(target: "app_events", Level::INFO, "something has happened 1!");
// 进入 span ( 开始 )

@ -1,80 +1,84 @@
# Rust语言周刊
# Rust 语言周刊」 第 16 期 · 2022-06-24
Rust语言周刊精选全世界过去一周的优秀文章、新闻、开源项目和语言动态。
本周刊由 RustCn 倾情打造,其中, `[Zh]` 标识的中文资料由 Rust 翻译计划提供,并且原始的 Markdown 文档已[全部开源](https://github.com/rustlang-cn/rustt),欢迎大家阅读和订阅。
> RustCnhttps://hirust.cn, 公众号: Rust语言中文网
# 「Rust 语言周刊」 第 15 期 · 2022-06-10
<img src="https://pic3.zhimg.com/80/v2-c7576bc34d1da15c96032aa39a6a1796_1440w.jpeg">
<h5 align="center">题图: Rust 好难啊</h5>
## 官方新闻
1、[首期 Rust 基金会的赞助名单已经出炉](https://foundation.rust-lang.org/news/2022-06-14-community-grants-program-awards-announcement/)
按照目前的计划,基金会每年开放两次申请,每次 20 万美元的总额( 估计会逐年增加 ),大致授予[合作伙伴](https://foundation.rust-lang.org/news/2022-06-14-community-grants-program-awards-announcement/#fellowship-awards)和[个人项目](https://foundation.rust-lang.org/news/2022-06-14-community-grants-program-awards-announcement/#individual-project-grant-awards)两个方向。
下次的申请开放大概在 10 月份左右,强烈建议有能力的都去试试,万一申请下来,几千美元也是很香的 :)
## 精选文章
1、[简单的 Rust 面试问题](https://flakm.github.io/posts/rust_interview_questions/)
1、[Zh] [用 Rust 从 0 到 1 实现一个最简化的 KV 存储引擎](https://blog.csdn.net/qq_36456827/article/details/125304612)
这种有讲有练的干货真的不多( 大部分人貌似都更喜欢做一些偏软货的东东,包括我自己 :P ),本文不仅内容质量高,而且还随文附送一个相当不错的 DB demo 实现,非常值得阅读和学习!
2、[智商提升了一岁的 Rust 编译检查器](https://jackh726.github.io/rust/2022/06/10/nll-stabilization.html)
当你好奇的看到这里时,和我一样,你被作者的标题忽悠了,其实是 Rust 编译检查器在最新版本中会默认开启所有的功能。不过在文章中,还讲到了很多生命周期错误提示的最新变动以及未来的展望,总算没白来了 :D
3、[大佬来找茬系列之教学明星项目 mini-redis](https://smallcultfollowing.com/babysteps/blog/2022/06/13/async-cancellation-a-case-study-of-pub-sub-in-mini-redis/)
当年(2015)我搜索 Go 语言工作时有多绝望,你现在搜索 Rust 工作就有多绝望再优秀的语言要在行业里流行开来总需要时间的沉淀不管如何Rust 现在正在走在一条正确的快行道上。
一般来说,在社区中推荐一个新手学习项目, [`mini-redis`](https://github.com/tokio-rs/mini-redis) 往往是首选,它拥有高质量且简洁的代码实现、清晰完善的文档以及对异步编程保姆式的引导讲解,总之非常棒
因此,无论是面试官还是面试者,提前储备一些 Rust 的面试题,都是不错的选择。
但是它没有缺点吗这不Baby steps 大佬出手了,干货满满,强烈推荐阅读!
2、[来自强类型的诅咒](https://fasterthanli.me/articles/the-curse-of-strong-typing)
4、[如何让 Rust 项目发生内存泄漏甚至崩溃?](https://fly.io/blog/rust-memory-leak/)
我骑着马儿,穿过了雪山,来到了草原,遇到了美丽的...错误?!大家写 Rust 有没有这种感觉,从题目可以看出,作者是绝对有这种感觉的,特别是在他的 Boss 宣称:从今以后,我们的一切都要使用 Rust 后...
别开枪,是友军!事实上,这篇文章讲的是如何修复 Rust 项目的内存泄漏问题,具体的项目是 `fly.io` 公司的代理服务,该代理会将来自世界各地的用户请求路由到最适合的虚拟机中( 虚拟机分布在全世界 21 个地区 )。
3、[测量 Rust 中的堆内存分配](https://flakm.github.io/posts/heap_allocation/)
5、[使用自建的 prelude.rs 来简化 Rust 项目的依赖引入](https://justinwoodring.com/blog/tidy-your-rust-imports-with-prelude/)
如果问程序员,为何要节省内存,他会说这是技术的体现;如果问老板,为何要节省内存,他会说这是因为穷。总是,在节省硬件成本这件事上,大家的目标出奇的一致。那么现在的问题就是:该如何衡量应用的内存使用情况?
Rust 项目大了后,一个不得不面对的问题就是:该如何处理每个文件中都需要导入的重复依赖,例如:
4、[Arc 和 Mutex](https://itsallaboutthebit.com/arc-mutex/)
```rust
use crate::models::clubs_md::{Club, NewClub, ClubDetails};
use crate::models::users_md::{User, UserDetails, NewUser, Admin};
use crate::models::club_members_md::{MembershipStatus, ClubMember, NewClubMember};
use crate::Db;
```
这篇文章讲解了一个很有趣的点:`Arc` 为何要求其包裹的类型实现 `Sync` 特征,值得一看!
6、[使用 Rust 来测试 NVDIA 的 GPU 性能](https://simbleau.github.io/blog/gpu-profiling-with-rust/)
5、[使用 Github Actions 让 Rust 构建提速 30 倍](https://ectobit.com/blog/speed-up-github-actions-rust-pipelines/)
在目前的 CPU、GPU 跑分领域Rust 还是小兵之姿,但将军的梦想还是要有的。这篇文章中,作者将使用 NVDIA 的工具扩展 SDK 来开发基于 Rust 的 GPU 和 CPU 跑分工具,喜欢猎奇的同学不容错过。
Rust 什么都好,就是编译太慢了,特别是你同时写 Go 和 Rust 时,那种对比体验更是明显。原因在于,在编译过程中,为了安全性和性能 Rust 会检查很多很多东西,何况 Rust 提供的语言特性也远比 Go 要更加丰富。
当然,这个过程是可以提速的,例如在 `Cargo.toml` 中设置编译优化选项,再比如本文中的构建缓存。
## 开源项目
1、 [lurk: 使用 Rust 重新实现 strace](https://jakobwaibel.com/2022/06/06/ptrace/)
6、[Rust 好难啊](https://hirrolot.github.io/posts/rust-is-hard-or-the-misery-of-mainstream-programming.html)
目前项目还较为早期,但是关于项目的愿景,作者描述的较为清晰,这种项目一般都是值得期待并能产生结果的。
Rust 之所以给很多人难学的印象,很重要的一点就在于:某些其它语言很轻松就能处理的问题,在 Rust 中,你需要兼具美貌、智慧与勇气,才能搞定。
2、[error-stack: 一个全新的 Rust 错误处理库](https://hash.dev/blog/announcing-error-stack)
大家可能以为这篇文章是一个新手写的,其实不然,作者已经浸淫 Rust 数年,还在某次大会上分享过 Rust但是他依然会遇到一些意料之外的棘手错误一起来看看吧
该库支持上下文感知和任意用户数据的关联。事实上,这种类型的开源项目往往都来源于一个团队在实际项目中遇到的痛点,因此工程性适用性会非常强。总之,以我个人的经验,不以 star 论英雄,还是看看是 `谁` 开源了 `什么` 项目以及适不适合 `你` 的使用场景,很多低星项目其实是非常优秀和成熟的,只不过没有得到应有的曝光度
7、[爆发和挑战并存](https://thestack.technology/rust-language-explosive-growth-challenges-rust-governance/)
3、[打遍 playground 无敌手的 Rust Explorer](https://www.rustexplorer.com/b/about)
在过去 24 个月中Rust 开发者的数量增加了 3 倍,可以说从 Rust 基金会成立后Rust 一直在爆发式增长,但是其所面临的挑战也越来越大
这个项目真的非常吸引我,不知道大家对于在线代码运行的期许是什么,对我而言,能够运行各种代码绝对是最大的亮点,而这个项目支持 10000 个三方依赖库(crates.io 上的 top 10000)
8、[使用 Rust 来爬取网页](https://www.scrapingbee.com/blog/web-scraping-rust/)
不是 100也不是 1000而是 10000基本上这意味着几乎所有代码片段都可以在这里运行了也意味着我的 `practice.rs` 项目可以进一步扩展Bingo!
想从某些网站获取信息,一般有两个途径:首先就是调用网站提供的 API这也是最安全、最合法的方式(特别是国内!),例如 Github 就提供了异常丰富的 API其次就是使用爬虫来爬取到网页后再对内容进行解析以提取出有用的信息。
4、[clap 3.2 发布](https://epage.github.io/blog/2022/06/clap-32-last-call-before-40/)
9、[Video][使用 Rust 来编写 WGPU 程序](https://www.youtube.com/playlist?list=PL_UrKDEhALdJS0VrLPn7dqC5A4W1vCAUT)
看起来只是一个小版本,但是它可是 4.0 大版本的预演,因此新增了两种 API 构建方式。
5、[c2rust 王者归来](https://immunant.com/blog/2022/06/back/)
这个项目对于 Rust 的生态而言太太太重要了要进一步扩大自己的份额C 语言就是绕不过的高山,而重写旧项目在大多数时候显然都是不现实的,因此 `c2rust` 才会有这么高的呼声,只不过之前因为核心作者的原因,项目几乎不怎么活跃了,还好现在 `immunant` 公司接过了大旗。
## 往期回顾
要我说,这种底层类型的大型开源项目要做得好,还是得背靠大树,例如之前的 [`cc`](https://github.com/rust-lang/cc-rs/issues/663) 项目就是从 `alex` 个人名下转到了 `rust` 官方组织下,最终又焕发了第二春。
目前所有的周刊都按照 `年/月/日期` 的方式归纳在 [docs](./docs) 目录下,大家可以按需查看。
6、[mold 发布 0.3 版本](https://github.com/rui314/mold)
- [第 14 期](./docs/2022/5月/29.md)
- [第 13 期](./docs/2022/5月/22.md)
- [第 12 期](./docs/2022/5月/16.md)
- [第 11 期](./docs/2022/5月/07.md)
- [第 10 期](./docs/2022/4月/29.md)
- [第 9 期](./docs/2022/4月/24.md)
- [第 8 期](./docs/2022/4月/15.md)
- [第 7 期](./docs/2022/4月/08.md)
- [第 6 期](./docs/2022/4月/02.md)
- [第 5 期](./docs/2022/3月/25.md)
- [第 4 期](./docs/2022/3月/18.md)
- [第 3 期](./docs/2022/3月/11.md)
- [第 2 期](./docs/2022/3月/04.md)
- [第 1 期](./docs/2022/2月/28.md)
mold 是一个用 Rust 写的链接器,它的性能比 LLVM 的 lld 链接器快好几倍,目标是通过降低编译时间来让 Rust 项目的开发闭环周期大幅提速。以下是性能对比图:
<img src="https://pic1.zhimg.com/80/v2-62f5d64e110fb3c6a9af669ccb1357f7_1440w.png" />
## 怀揣劲爆消息或优质内容?
欢迎提交 `issue``PR`,我们欢迎一切有价值的内容,并且你提供的每条消息都将标注上你的 github 用户名和链接。

@ -501,7 +501,8 @@ test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; fini
其中还有一点值得注意,那就是测试模块 `tests` 的名称也出现在了最终结果中:`tests::add_two_and_two`,这是非常贴心的细节,也意味着我们可以通过**模块名称来过滤测试**
```shell
cargo test tests
$ cargo test tests
running 3 tests
test tests::add_two_and_two ... ok
test tests::add_three_and_two ... ok

@ -189,7 +189,7 @@ mod test {
```
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -85,7 +85,7 @@ mod test {
}
```
```shell
> cargo test
$ cargo test
running 18 tests
test fifth::test::into_iter ... ok
@ -149,7 +149,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
```
```shell
> cargo test
$ cargo test
error[E0521]: borrowed data escapes outside of closure
--> src\silly2.rs:47:32
@ -194,7 +194,7 @@ fn elegance() {
```
```shell
> cargo test
$ cargo test
error[E0521]: borrowed data escapes outside of closure
--> src\silly2.rs:46:17
@ -296,7 +296,7 @@ fn cell() {
```
```shell
> cargo test
$ cargo test
running 19 tests
test fifth::test::into_iter ... ok

@ -170,7 +170,7 @@ pub fn pop(&mut self) -> Option<i32> {
以上代码果不其然又报错了:
```shell
> cargo build
$ cargo build
error[E0507]: cannot move out of borrowed content
--> src/first.rs:28:15

@ -43,7 +43,7 @@ mod test {
`src/first.rs` 中添加以上测试模块,然后使用 `cargo test` 运行相关的测试用例:
```shell
> cargo test
$ cargo test
error[E0433]: failed to resolve: use of undeclared type or module `List`
--> src/first.rs:43:24
@ -150,7 +150,7 @@ impl Drop for List {
测试下上面的实现以及之前的长链表例子:
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -48,7 +48,7 @@ pub enum List {
```
```shell
> cargo build
$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.22s
```

@ -51,7 +51,7 @@ fn into_iter() {
```
```shell
cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a
@ -84,7 +84,7 @@ impl<T> List<T> {
```
```shell
> cargo build
$ cargo build
```
迄今为止一切运行正常,接下来的 `next` 实现起来会有些麻烦:
@ -101,7 +101,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
```
```shell
cargo build
$ cargo build
error[E0521]: borrowed data escapes outside of closure
--> src/fourth.rs:155:13
@ -157,7 +157,8 @@ fn next(&mut self) -> Option<Self::Item> {
```
```shell
cargo build
$ cargo build
Compiling lists v0.1.0 (/Users/ABeingessner/dev/temp/lists)
error[E0521]: borrowed data escapes outside of closure
--> src/fourth.rs:159:13

@ -57,7 +57,7 @@ impl<T> List<T> {
除此之外,我们还需要处理一些关于空链表的边界问题:对于绝大部分操作而言,可能只需要使用 `head``tail` 指针,但是对于空链表,则需要同时使用它们。
一个验证方法 `methods` 是否有效的办法就是看它是否能保持不变性, 每个节点都应该有两个指针指向它: 中间的节点被它前后的节点所指向,而头部的节点除了被它后面的节点所指向外,还会被链表本身所指向,尾部的节点亦是如此。
一个验证方法 `methods` 是否有效的办法就是看它是否能保持不变性, 每个节点都应该有两个指针指向它: 中间的节点被它前后的节点所指向,而头部的节点除了被它后面的节点所指向外,还会被链表本身所指向,尾部的节点亦是如此。
```rust
pub fn push_front(&mut self, elem: T) {
@ -79,7 +79,7 @@ pub fn push_front(&mut self, elem: T) {
```
```rust
cargo build
> cargo build
error[E0609]: no field `prev` on type `std::rc::Rc<std::cell::RefCell<fourth::Node<T>>>`
--> src/fourth.rs:39:26
@ -115,7 +115,7 @@ pub fn push_front(&mut self, elem: T) {
```
```shell
> cargo build
$ cargo build
warning: field is never used: `elem`
--> src/fourth.rs:12:5
@ -152,7 +152,7 @@ pub fn pop_front(&mut self) -> Option<T> {
```
```shell
> cargo build
$ cargo build
error[E0609]: no field `elem` on type `std::rc::Rc<std::cell::RefCell<fourth::Node<T>>>`
--> src/fourth.rs:64:22
@ -180,7 +180,7 @@ pub fn pop_front(&mut self) -> Option<T> {
```
```shell
cargo build
$ cargo build
error[E0507]: cannot move out of borrowed content
--> src/fourth.rs:64:13
@ -203,7 +203,7 @@ old_head.into_inner().elem
```
```shell
> cargo build
$ cargo build
error[E0507]: cannot move out of an `Rc`
--> src/fourth.rs:64:13
@ -222,7 +222,7 @@ Rc::try_unwrap(old_head).unwrap().into_inner().elem
`Rc::try_unwrap` 返回一个 `Result`,由于我们不关心 `Err` 的情况( 如果代码合理,这里不会是 `Err` ),直接使用 `unwrap` 即可。
```shell
> cargo build
$ cargo build
error[E0599]: no method named `unwrap` found for type `std::result::Result<std::cell::RefCell<fourth::Node<T>>, std::rc::Rc<std::cell::RefCell<fourth::Node<T>>>>` in the current scope
--> src/fourth.rs:64:38
@ -242,7 +242,7 @@ Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem
```
```shell
cargo build
$ cargo build
```
终于成功的运行了,下面依然是惯例 - 写几个测试用例 :
@ -283,7 +283,7 @@ mod test {
```
```shell
cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a
@ -302,7 +302,7 @@ test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured
```
## Drop
在[循环引用章节](),我们介绍过 `Rc` 最怕的就是引用形成循环,而双向链表恰恰如此。因此,当使用默认的实现来 `drop` 我们的链表时,两个节点会将各自的引用计数减少到 1 然后就不会继续减少,最终造成内存泄漏。
在[循环引用章节](),我们介绍过 `Rc` 最怕的就是引用形成循环,而双向链表恰恰如此。因此,当使用默认的实现来 `drop` 我们的链表时,两个节点会将各自的引用计数减少到 1 然后就不会继续减少,最终造成内存泄漏。
所以,这里最好的实现就是将每个节点 `pop` 出去,直到获得 `None`:
```rust

@ -20,7 +20,7 @@ pub fn peek_front(&self) -> Option<&T> {
```
```shell
cargo build
$ cargo build
error[E0515]: cannot return value referencing temporary value
--> src/fourth.rs:66:13
@ -58,7 +58,7 @@ pub fn peek_front(&self) -> Option<Ref<T>> {
```
```shell
> cargo build
$ cargo build
error[E0308]: mismatched types
--> src/fourth.rs:64:9
@ -94,7 +94,7 @@ pub fn peek_front(&self) -> Option<Ref<T>> {
```
```shell
> cargo build
$ cargo build
```
Gooood! 本章节的编译错误可以说是多个链表中最难解决的之一,依然被我们成功搞定了!
@ -114,7 +114,7 @@ fn peek() {
```
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -139,7 +139,7 @@ fn peek() {
什么?你问我这里的测试用例全吗?只能说如果测试全部的组合情况,这一章节会被撑爆。至于现在,能不出错就谢天谢地了 :(
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -18,8 +18,8 @@
#### 创建一个项目
在开始前,先来创建一个项目专门用于链表学习:
```shell
> cargo new --lib lists
> cd lists
$ cargo new --lib lists
$ cd lists
```
之后,我们会将每个一个链表放入单独的文件中,需要注意的是我们会尽量模拟真实的 Rust 开发场景:你写了一段代码,然后编译器开始跳出试图教你做事,只有这样才能真正学会 Rust温室环境是无法培养出强大的 Rustacean 的。

@ -55,7 +55,7 @@ fn into_iter() {
```
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a
@ -97,7 +97,7 @@ impl<T> Iterator for Iter<T> {
```
```shell
> cargo build
$ cargo build
error[E0106]: missing lifetime specifier
--> src/second.rs:72:18
@ -122,7 +122,7 @@ pub struct Iter<'a, T> {
```
```shell
> cargo build
$ cargo build
error[E0106]: missing lifetime specifier
--> src/second.rs:83:22
@ -163,7 +163,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
```
```shell
> cargo build
$ cargo build
error: expected `:`, found `node`
--> src/second.rs:77:47
@ -217,7 +217,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
现在,我们也许可以自信的编译下试试了:
```shell
> cargo build
$ cargo build
error[E0308]: mismatched types
--> src/second.rs:77:22
@ -260,7 +260,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
```
```shell
> cargo build
$ cargo build
Compiling lists v0.1.0 (/Users/ABeingessner/dev/temp/lists)
error[E0515]: cannot return reference to local data `*node`
@ -315,7 +315,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
```
```shell
> cargo build
$ cargo build
Compiling lists v0.1.0 (/Users/ABeingessner/dev/temp/lists)
error[E0308]: mismatched types
@ -364,7 +364,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
```
```shell
> cargo build
$ cargo build
```
🎉 🎉 🎉
@ -402,7 +402,7 @@ fn iter() {
```
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -57,7 +57,7 @@ impl<'a, T> Iterator for IterMut<'a, T> {
```
```shell
> cargo build
$ cargo build
error[E0596]: cannot borrow `self.head` as mutable, as it is behind a `&` reference
--> src/second.rs:95:25
@ -96,7 +96,7 @@ fn next(&mut self) -> Option<Self::Item> {
```
```shell
> cargo build
$ cargo build
```
老规矩,来测试下:
@ -114,7 +114,7 @@ fn iter_mut() {
```
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -12,7 +12,7 @@ pub fn peek(&self) -> Option<&T> {
```
```shell
> cargo build
$ cargo build
error[E0515]: cannot return reference to local data `node.elem`
--> src/second.rs:37:13
@ -46,7 +46,7 @@ pub fn peek(&self) -> Option<&T> {
```
```shell
> cargo build
$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
```
@ -81,7 +81,7 @@ fn peek() {
```
```shell
> cargo test
$ cargo test
error[E0384]: cannot assign twice to immutable variable `value`
--> src/second.rs:100:13
@ -124,7 +124,7 @@ fn peek() {
这次我们直接匹配出来可变引用 `value`,然后对其修改即可。
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -126,7 +126,7 @@ pub fn pop(&mut self) -> Option<i32> {
不错,看上去简洁了很多,下面运行下测试代码确保链表依然可以正常运行(这就是 TDD 的优点!) :
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a
@ -188,7 +188,7 @@ impl<T> Drop for List<T> {
大家在修改了 `List` 的定义后,别忘了将 `impl` 中的 `List` 修改为 `List<T>`,切记**泛型参数也是类型定义的一部分**。
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -49,7 +49,8 @@ list3 -> X ---+
测试下新的代码:
```shell
cargo test
$ cargo test
Compiling lists v0.1.0 (/Users/ABeingessner/dev/too-many-lists/lists)
Finished dev [unoptimized + debuginfo] target(s) in 1.10s
Running /Users/ABeingessner/dev/too-many-lists/lists/target/debug/deps/lists-86544f1d97438f1f

@ -74,7 +74,7 @@ pub fn prepend(&self, elem: T) -> List<T> {
运行下试试:
```shell
> cargo build
$ cargo build
warning: field is never used: `elem`
--> src/third.rs:10:5
@ -101,7 +101,7 @@ pub fn tail(&self) -> List<T> {
```
```shell
cargo build
$ cargo build
error[E0308]: mismatched types
--> src/third.rs:27:22
@ -159,7 +159,7 @@ mod test {
```
```shell
> cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a
@ -211,7 +211,7 @@ fn iter() {
```
```shell
cargo test
$ cargo test
Running target/debug/lists-5c71138492ad4b4a

@ -13,7 +13,7 @@ impl<T> List<T> {
但是我们不再在 `tail` 中使用 `Option`:
```shell
> cargo build
$ cargo build
error[E0308]: mismatched types
--> src/fifth.rs:15:34
@ -73,7 +73,7 @@ pub fn push(&mut self, elem: T) {
```
```shell
> cargo build
$ cargo build
error[E0609]: no field `next` on type `*mut fifth::Node<T>`
--> src/fifth.rs:31:23
@ -91,7 +91,7 @@ error[E0609]: no field `next` on type `*mut fifth::Node<T>`
```
```shell
> cargo build
$ cargo build
error[E0609]: no field `next` on type `*mut fifth::Node<T>`
--> src/fifth.rs:31:23
@ -109,7 +109,7 @@ error[E0609]: no field `next` on type `*mut fifth::Node<T>`
```
```shell
> cargo build
$ cargo build
error[E0133]: dereference of raw pointer is unsafe and requires
unsafe function or block
@ -149,7 +149,7 @@ pub fn push(&mut self, elem: T) {
```
```shell
> cargo build
$ cargo build
warning: field is never used: `elem`
--> src/fifth.rs:11:5
|
@ -225,7 +225,7 @@ mod test {
摊牌了,我们偷懒了,这些测试就是从之前的栈链表赋值过来的,但是依然做了些改变,例如在末尾增加了几个步骤以确保在 `pop` 中不会发生尾指针损坏( tail-pointer corruption )的情况。
```shell
cargo test
$ cargo test
running 12 tests
test fifth::test::basics ... ok

@ -82,7 +82,7 @@ impl<T> List<T> {
但是如果你担心不再能看到错误,那就纯属多余了:
```shell
> cargo build
$ cargo build
error[E0382]: use of moved value: `new_tail`
--> src/fifth.rs:38:38
@ -139,7 +139,7 @@ impl<T> List<T> {
```
```shell
> cargo build
$ cargo build
error[E0106]: missing lifetime specifier
--> src/fifth.rs:3:18
@ -190,7 +190,7 @@ impl<'a, T> List<'a, T> {
```
```shell
cargo build
$ cargo build
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/fifth.rs:35:27
@ -235,7 +235,7 @@ pub fn push(&'a mut self, elem: T) {
当当当当,成功通过编译:
```shell
cargo build
$ cargo build
warning: field is never used: `elem`
--> src/fifth.rs:9:5
@ -297,7 +297,7 @@ mod test {
}
```
```shell
cargo test
$ cargo test
error[E0499]: cannot borrow `list` as mutable more than once at a time
--> src/fifth.rs:68:9

@ -196,7 +196,7 @@ mod test {
```
```shell
cargo test
$ cargo test
running 12 tests
test fifth::test::basics ... ok

@ -36,7 +36,7 @@ info: installing component 'miri'
> + 是一种临时性的规则运用,如果你不想每次都使用 `+nightly-2022-01-21`,可以使用 [`rustup override set`](https://course.rs/appendix/rust-version.html#rustup-和-rust-nightly-的职责) 命令对当前项目的 Rust 版本进行覆盖
```shell
> cargo +nightly-2022-01-21 miri test
$ cargo +nightly-2022-01-21 miri test
I will run `"cargo.exe" "install" "xargo"` to install
a recent enough xargo. Proceed? [Y/n]
@ -146,7 +146,7 @@ UB 检测是必须的,因为它发生在运行时,因此很难发现,如
总之,`miri` 的使用很简单:
```shell
> cargo +nightly-2022-01-21 miri test
$ cargo +nightly-2022-01-21 miri test
```
下面来看看具体的错误:

@ -50,7 +50,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
Compiling miri-sandbox v0.1.0
Finished dev [unoptimized + debuginfo] target(s) in 0.71s
Running `target\debug\miri-sandbox.exe`
@ -101,7 +102,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
22
```
@ -141,7 +143,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
20
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run
@ -172,7 +175,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
[3, 3, 0, 0, 0, 0, 0, 0, 0, 0]
```
@ -206,7 +210,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
[6, 0, 0, 0, 0, 0, 0, 0, 0, 0]
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run
@ -238,7 +243,7 @@ unsafe {
```
```shell
cargo run
$ cargo run
[20, 0, 0, 0, 0, 0, 0, 0, 0, 0]
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run
@ -310,7 +315,7 @@ unsafe {
```
```shell
cargo run
$ cargo run
[10, 12, 0, 0, 0, 0, 0, 0, 0, 0]
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run
@ -353,7 +358,7 @@ unsafe {
```
```shell
cargo run
$ cargo run
[8, 12, 4, 6, 8, 10, 12, 14, 16, 18]
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run
@ -393,7 +398,7 @@ unsafe {
```
```shell
cargo run
$ cargo run
warning: unnecessary `unsafe` block
--> src\main.rs:6:1
@ -436,7 +441,7 @@ unsafe {
```
```shell
cargo run
$ cargo run
error[E0606]: casting `&i32` as `*mut i32` is invalid
--> src/main.rs:11:20
@ -454,7 +459,7 @@ let ptr4 = sref3 as *const i32 as *mut i32;
如上,先将不可变引用转换成不可变的裸指针,然后再转换成可变的裸指针。
```shell
cargo run
$ cargo run
14
17
@ -502,7 +507,8 @@ unsafe {
可以看到,我们其实可以创建一个可变的裸指针,只要不去使用写操作,而是只使用读操作。
```shell
cargo run
$ cargo run
10
10
13
@ -534,7 +540,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
12
13
@ -578,7 +585,8 @@ unsafe {
地狱一般的代码,就等着 miri 来优化你吧。
```shell
cargo run
$ cargo run
16
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run
@ -624,7 +632,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
12
16
@ -666,7 +675,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
13
16
@ -694,7 +704,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
21
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run
@ -726,7 +737,8 @@ unsafe {
```
```shell
cargo run
$ cargo run
21
MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri run

Loading…
Cancel
Save