Skip to content

Commit

Permalink
Finished re-constructure
Browse files Browse the repository at this point in the history
  • Loading branch information
gnu4cn committed Dec 1, 2023
1 parent e3b5cc8 commit ecd2e9e
Show file tree
Hide file tree
Showing 15 changed files with 2,627 additions and 2,556 deletions.
1,613 changes: 5 additions & 1,608 deletions src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md

Large diffs are not rendered by default.

948 changes: 0 additions & 948 deletions src/Ch21_Appendix.md

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/SUMMARY.md
Expand Up @@ -134,5 +134,17 @@
- [关于宏](advanced_features/macros.md)

- [最后项目:构建一个多线程的 Web 服务器](Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md)
- [构建一个单线程的 Web 服务器](final_project/single-threaded.md)
- [将这个单线程服务器修改为多线程服务器](final_project/multithreaded.md)
- [优雅关机与内存清理](final_project/graceful_shutdown.md)

- [附录](Ch21_Appendix.md)
- [A - 关键字](appendix/keywords.md)
- [B - 运算符与符号](appendix/ops_and_symbols.md)
- [C - 派生特质](appendix/derivable_traits.md)
- [D - 有用开发工具](appendix/dev_tools.md)
- [E - 关于版本](appendix/editions.md)
- [F - 本书的一些译本](appendix/translations.md)
- [G - Rust 是怎样构造出来的与 “每日 Rust”](appendix/releases.md)
- [H - 有用笔记](appendix/notes.md)
- [I - 术语清单](appendix/terminology_list.md)
116 changes: 116 additions & 0 deletions src/appdendix/derivable_traits.md
@@ -0,0 +1,116 @@
# 附录 C:派生特质

**Appendix C: Derivable Traits**


本书的多个不同地方,咱们都曾讨论过 `derive` 属性,咱们可将其应用到结构体或枚举定义。`derive` 属性会在咱们以 `derive` 语法注解的类型上,生成将以某个特质自身默认实现,而实现该特质的代码。

在这个附录中,咱们会提供到标准库中,咱们可以与 `derive` 一起使用的全部特质的参考。以下各个小节均会讲到:

- 此特质将启用那些操作符与方法;

-`derive` 所提供到的该特质实现会做些什么;

- 实现该特质对那个类型意味着什么;

- 允许及不允许实现该特质的情况;

- 需要该特质操作的示例。



若咱们想要不同于由 `derive` 属性所提供的行为,请参考 [标准库文档](https://doc.rust-lang.org/std/index.html),了解如何亲自实现各个特质的详细信息。

这里列出的这些特质,只是一些由标准库所提供的,可使用 `derive` 实现于咱们类型上的那些。定义在标准库中别的一些特质,则没有什么合理的默认行为,因此是否要以对于咱们正尝试完成的东西有意义的方式,实现他们就取决于咱们自己了。

不能派生的一个特质示例便是 `Display`,其为终端用户处理格式化。咱们应始终要考虑将某个类型显示给用户的恰当方式。终端用户应被允许看到该类型的哪些部分?他们会发现哪些部分是相关的?数据的何种形式才是与他们最为密切相关的?Rust 编译器并无这种见解,因此他就无法为咱们提供到恰当的默认行为。

这个附录中所提供到的派生特质清单并不详尽:库可以为他们自己的特质实现 `derive`,从而领导咱们可使用 `derive` 的特质清单为真正开放的。实现 `derive` 设计到使用程序性宏,这在第 19 章的 [“关于宏”](Ch19_Advanced_Features.md#关于宏) 小节讲到过。


## 输出给编程者的 `Debug`

**`Debug` for Programmer Output**


`Debug` 特质实现了格式字符串中的格式化,所谓格式字符串,即咱们通过在 `{}` 里添加 `:?` 所表示的。

`Debug` 特质允许咱们为调试目的打印某种类型的实例,如此咱们以及用到咱们类型的其他编程者,就可以在程序执行的某个特定时刻,就其某个实例加以探查。

在比如用到 `assert_eq!` 宏中等情况下,`Debug` 特质便是要求使用的。`assert_eq!` 这个宏在相等断言失败时,就会打印出作为参数所给到的两个实例值,如此编程者就可以看到为何这两个实例不相等。


## 用于相等比较的 `PartialEq``Eq`


`PartialEq` 特质允许咱们比较某种类型的两个实例,来检查他们是否相等,并实现 `==``!=` 运算符的应用。

`PartialEq` 进行派生,就会实现 `eq` 方法。当 `ParitalEq` 实在结构体上实现的时,只有在两个实例的 *全部* 字段都相等时,他们才是相等的,且在有任何字段不等时,两个实例便不相等。当在枚举上派生时,枚举的各个变种与自身相等,而不等于其他任何变种。

在使用需要能够比较某个类型的两个实例是否相等的 `assert_eq!` 宏时,就需要这个 `PartialEq` 特质。

`Eq` 特质则没有方法。他的目的是要表明,所注解的类型的每个值,其值都等于他自身。尽管并非所有实现 `PartialEq` 的类型都可以实现 `Eq`,但 `Eq` 特质却只可应用到那些同时实现了 `PartialEq` 的类型。这方面的一个示例,便是浮点数类型:浮点数的实现,就表明两个非数字(the not-a-number, `NaN`)的值,是各自不相等的。

要求 `Eq` 的一个示例,就是 `HashMap<K, V>` 中的那些键,如此 `HashMap<K, V>` 就可以区分出两个键是否一致。


## 用于排序比较的 `PartialOrd``Ord`


**`PartialOrd` and `Ord` for Ordering Comparisons**

`PartialOrd` 特质实现为排序目的,而比较某种类型的那些实例。实现了 `PartialOrd` 的类型,便可与 `<``>``<=``>=` 符号一起使用了。咱们只能对那些同时实现了 `PartialEq` 的类型,应用这个 `PartialOrd` 特质。

派生 `PartialOrd`,会实现 `partial_cmp` 方法,该方法会返回一个在所给的那些值不会产生出顺序时,将为 `None` 的一个 `Option<Ordering>`。至于即使那种类型的大多数值都可被比较,但仍不会产生出顺序的值的一个示例,便是非数字(`NaN`)浮点值。在任何浮点数和非数字浮点值下调用 `partial_cmp`,都会返回 `None`

在于结构体上派生时,`PartialOrd` 会通过字段出现在结构体定义中的顺序,比较每个字段中的值,比较两个实例。而当于枚举上派生时,枚举定义中较早声明的枚举变种,被当作是小于后面所列出的那些变种的。

在比如会产生出由范围表达式所指定范围中一个随机数的, `rand` 代码箱的 `gen_range` 方法来说,`PartialOrd` 特质便是需要的。

`Ord` 特质实现对所注解类型的任何两个值,将存在有效顺序的掌握。`Ord` 特质会实现 `cmp` 方法,由于有效排序将始终可行,因此该方法返回的是 `Ordering` 而非 `Option<Ordering>`。咱们只可对那些同时实现了 `PartialOrd``Eq` (而 `Eq` 要求 `PartialEq`) 的类型,实现这个 `Ord` 特质。当于结构体及枚举上派生 `Ord` 时,`cmp` 就会以与 `PartialOrd``partial_cmp` 的派生实现同样方式行事。

要求 `Ord` 的一个示例,即为将一些值存储在 `BTreeSet<T>` 这种根据值的排序,而存储数据的数据结构中时。


## 用于复制值的 `Clone``Copy`

**`Clone` and `Copy` for Duplicating Values**


`Clone` 特质实现了显式创建值的深拷贝,而该复制过程则可能涉及运行一些任意代码,arbitary code,与拷贝内存堆数据。请参阅第 4 章中 [“变量与数据交互方式:克隆”](Ch04_Understanding_Ownership.md#变量与数据交互方式之二克隆) 小节,了解更多有关 `Clone` 的信息。

派生 `Clone` 会实现 `clone` 方法,当对整个类型实现了这个方法时,其就会在该类型的各个部分上调用 `clone`。这意味着类型要派生 `Clone` 其中的全部字段或值,都必须同时实现 `Clone`

需要 `Clone` 特质的一个示例,便是在切片上调用 `to_vec` 方法时。切片不持有其包含的那些类型实例,但自 `to_vec` 所返回的那个矢量值,却将需要持有他的那些实例,从而 `to_vec` 会调用各个条目上的 `clone`。因此,存储在切片中的类型,就必须实现 `Clone`

`Copy` 特质实现了只通过拷贝存储在栈上的二进制位,而复制某个值;任意代码并无必要。请参阅第 4 章中 [“唯栈数据:拷贝”](Ch04_Understanding_Ownership.md#唯栈数据拷贝stack-only-data-copy),了解更多有关 `Copy` 的信息。

`Copy` 特质没有定义阻止编程者过载那些方法,及破坏不会有任意代码运行这个假设的任何方法。那样的话,所有编程者就都可以假定,拷贝值将会非常快。

咱们可在其组成部分都实现了 `Copy` 的任何类型上派生 `Copy` 特质。由于实现 `Copy` 的类型,都有着执行与 `Copy` 同样任务的一个 `Clone` 的简单实现,因此实现 `Copy` 的类型必须同时实现 `Clone`

很少需要 `Copy` 特质;实现了 `Copy` 的类型,有着可供选择的优化方案,意味着咱们不必调用 `clone`,而调用 `clone` 会令到代码更简洁。

对于 `Copy` 下每种可能情况,咱们都可同时以 `Clone` 完成,除了代码可能更慢,或在一些地方不得不使用 `clone`


## 用于将值映射到固定大小值的 `Hash`

**`Hash` for Mapping a Value to a Value of Fixed Size**


`Hash` 特质实现了取某种任意大小类型的实例,并通过使用散列函数,将那个实例映射到固定大小的值。派生 `Hash` 会实现 `hash` 方法。`hash` 放的派生实现,会将在该类型各个组成部分上调用 `hash` 的结果结合起来,这就意味着类型要派生 `Hash`,那么其全部字段,都必须同时实现 `Hash`

要求 `Hash` 的一个示例,便是为了高效地存储数据,而在 `Hash<K, V>` 中存储那些键时。


## 用于默认值的 `Default`

**`Default` for Default Values**


`Default` 特质实现了为类型创建出一个默认值。派生 `Default` 会实现 `default` 函数。`default` 函数的派生实现,会在类型的各个部分上调用 `default` 函数,意味类型要派生 `Defualt`,其中的全部字段或值,都必须同时实现 `Default`

`Default::default` 函数,通常是与第 5 章中 [“使用结构体更新语法从其他实例创建出实例”](Ch05_Using_Structs_to_Structure_Related_Data.md#使用结构体更新语法从其他实例创建出实例) 小节里曾讨论过的结构体更新语法结合使用的。咱们可以定制结构体的几个字段,并在随后通过使用 `..Default::default()`,为其余字段设置并使用默认值。

`Option<T>` 实例上使用 `unwrap_or_default` 方法时,便是需要 `Default` 特质的一个示例。当那个 `Option<T>``None` 时,方法 `unwrap_or_default` 就将返回存储在 `Option<T>` 中,那个类型 `T``Default::default` 结果。
162 changes: 162 additions & 0 deletions src/appdendix/dev_tools.md
@@ -0,0 +1,162 @@
# 附录 D:一些有用开发工具

在此附录中,咱们会讲到 Rust 项目所提供的一些有用的开发工具。咱们将看看自动格式化、应用警告修复的一些快速方法、一种代码静态分析工具,a linter,以及与多种 IDE 的集成。


## 使用 `rustfmt` 的自动格式化

**Automatic Formatting with `rustfmt`**


`rustfmt` 工具会依据社区编码风格,重新格式化咱们的代码。许多协作项目,都使用了 `rustfmt` 来防止有关编写 Rust 时使用何种风格方面的争论:每个人都使用这个工具来格式化他们的代码。

要安装 `rustfmt`,请键入下面的命令:

```console
$ rustup component add rustfmt
```

如同 Rust 会同时给到 `rustc``cargo` 一样,此命令会给到咱们 `rustfmt``cargo-fmt`。要格式化任何 Cargo 项目,请敲入下面的命令:

```console
$ cargo fmt
```

运行此命令,会重新格式化当前代码箱中全部的 Rust 代码。这只会改变编码风格,而不会改变代码语义。关于 `rustfmt` 的更多信息,请参阅 [其文档](https://github.com/rust-lang/rustfmt).


## 使用 `rustfix` 修复咱们的代码

**Fix Your Code with `rustfix`**


`rustfix` 工具已被 Rust 安装所包含,并可大致以咱们想要的方式,修复那些有着明确纠正问题方法的一些编译器告警。咱们之前大概率已经见到过编译器告警了。比如,设想有下面这段代码:

文件名:`src/main.rs`

```rust
fn do_something() {}

fn main() {
for i in 0..100 {
do_something();
}
}
```

此处,咱们正调用 `do_something` 函数 100 次,但咱们在 `for` 循环的代码体中,从未用到那个变量 `i`。Rust 就会就此对咱们发出告警:

```console
$ cargo build
Compiling rustfix_demo v0.1.0 (/home/lenny.peng/rust-lang-zh_CN/rustfix_demo)
warning: unused variable: `i`
--> src/main.rs:4:9
|
4 | for i in 0..100 {
| ^ help: if this is intentional, prefix it with an underscore: `_i`
|
= note: `#[warn(unused_variables)]` on by default

warning: `rustfix_demo` (bin "rustfix_demo") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.29s
```

这个告警建议咱们要使用 `_i` 做名字:其中的下划线表示咱们有意不使用这个变量。通过运行 `cargo fix` 命令,咱们就可以使用 `rustfix`,自动应用那项建议:

```console
$ cargo fix --allow-no-vcs
Checking rustfix_demo v0.1.0 (/home/lenny.peng/rust-lang-zh_CN/rustfix_demo)
Fixed src/main.rs (1 fix)
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
```

当咱们再次看到 `src/main.rs`,就将发现 `cargo fix` 已修改了这段代码:

文件名:`src/main.rs`

```rust
fn do_something() {}

fn main() {
for _i in 0..100 {
do_something();
}
}
```

那个 `for` 循环变量,现在就被命名为了 `_i`,同时那条告警也不再出现了。

咱们还可使用 `cargo fix` 命令,将咱们的代码在不同 Rust 版本之间转换。有关这些 Rust 版本,在附录 E 中有讲到。


## 使用 Clippy 获得更多的代码静态分析

**More Lints with Clippy**

Clippy 工具是用于分析咱们代码,从而咱们可以捕获到一些常见错误,而改进咱们 Rust 代码的一套代码静态分析集合。

要安装 Clippy,请输入以下命令:

```console
$ rustup component add Clippy
```

在任何 Cargo 项目上要运行 Clippy 的静态分析,请输入以下命令:

```console
$ cargo clippy
```

比如说咱们编写了像下面这个程序这样,用到某个数学常量近似值,好比说 `pi`,的一个程序:

文件名:`src/main.rs`

```rust
fn main() {
let x = 3.1415;
let r = 8.0;
println!("圆的面积为 {}", x * r * r);
}
```

在这个项目上运行 `cargo clippy` 就会得到下面的报错:

```console
$ cargo clippy
Checking clippy_demo v0.1.0 (/home/lenny.peng/rust-lang-zh_CN/clippy_demo)
error: approximate value of `f{32, 64}::consts::PI` found
--> src/main.rs:2:13
|
2 | let x = 3.1415;
| ^^^^^^
|
= help: consider using the constant directly
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
= note: `#[deny(clippy::approx_constant)]` on by default

error: could not compile `clippy_demo` due to previous error
```

此报错让咱们明白,Rust 已经定义了一个更精确的 `PI` 常量,且当咱们使用这个常量时,咱们的程序将更为正确。那么咱们随后就应修改咱们代码为使用这个 `PI` 常量。下面的代码就捕获导致 Clippy 的任何错误或告警:

文件名:`src/main.rs`

```rust
fn main() {
let x = std::f64::consts::PI;
let r = 8.0;
println!("圆的面积为 {}", x * r * r);
}
```

有关 Clippy 的更多信息,请参阅 [其文档](https://github.com/rust-lang/rust-clippy)


## 用到 `rust-analyzer` 的 IDE 集成

**IDE Integration Using `rust-analyzer`**


为帮助 IDE 集成,Rust 社区建议使用 [`rust-analyzer`](https://rust-analyzer.github.io/)。此工具是一套以编译器为中心,操 [语言服务器协议,Language Server Protocol](http://langserver.org/) 的实用工具;而所谓语言服务器协议,则是用于各种 IDEs 和编程语言,二者相互之间通信的一种规格。有多种不同客户端可使用 `rust-analyzer`,比如 [Visual Studio Code 的 Rust 分析器插件](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)

请访问 `rust-analyzer` 项目 [主页](https://rust-analyzer.github.io/),了解其安全说明,随后在咱们的特定 IDE 中安装该语言的服务器支持。咱们的 IDE 就能获得诸如自动补全、跳至定义及行内报错等能力。
27 changes: 27 additions & 0 deletions src/appdendix/editions.md
@@ -0,0 +1,27 @@
# 附录 E:关于版本

**Appendix E - Editions**

在第一章中,咱们曾看到 `cargo new` 会把一点有关某个版的元数据,添加到咱们的 `Cargo.toml` 文件。此附录就会讲到那意味着什么!

Rust 语言及编译器有着六周的发布周期,意味着用户会得到源源不断的新功能。其他编程语言会不经常地发布较大变更;Rust 则会更频繁发布较小的更新。不久之后,全部这些小修改就会堆积起来。不过这一个个发布中,回头看看而讲到,“噢,从版本 1.10 到 1.31,Rust 改变了很多!”。则是不容易的。

每两三年,Rust 团队都会产生一个新的 Rust *版本,edition*。每个版本都会以完全更新的文档与工具,将那些业已落地到一个明确包中的特性放到一起。新版本会作为寻常的六周发布过程而交付。

这些版本服务了不同人群的不同目的:

- 对于活跃的 Rust 用户,新版本会把那些增量变更,一起放入到一个易于掌握的包中;
- 对于那些非用户,新版本释放了一些已落地的大进展信号,这会让 Rust 或许值得再看一看;
- 对于开发 Rust 的人们,新版本会提供这个项目作为整体的集结点。

在本书编写时,已有三个 Rust 版本可用:Rust 2015、Rust 2018 与 Rust 2021。本书是用 Rust 2021 版本的习惯用语编写的。

`Cargo.toml` 中的 `edition` 键,表示应对咱们的代码使用哪个版本的编译器。若该键不存在,Rust 就会以向后兼容原因,而使用 `2015` 作为版本值。

每个项目都可以选择一个不同于默认 2015 的版本。这些版本可能包含了不兼容的变更,比如包含了与代码中标识符冲突的新关键字。但是,除非咱们选到这些变更,那么即使咱们更新了所使用的 Rust 编译器,咱们的代码将继续编译。

全部 Rust 编译器版本,都会支持先于那个编译器发布而存在的任何版本,且他们可将任何受支持版本的代码箱连接起来。版本变更只会影响编译器于编译初期解析代码的方式。因此,当咱们正使用着 Rust 2015,而咱们的一项依赖使用了 Rust 2018 时,咱们的项目将编译,并能够使用那项依赖。与之相反,在咱们的项目使用 Rust 2018,而一项依赖使用了 Rust 2015 的情形下,也会工作。

要明确的是:绝大多数特性,在所有版本上都将可用。使用任何 Rust 版本的开发者,都将在新的稳定发布构造出来时,发现一些改进。但是,在一些情况下,主要是在新曾了关键字时,一些新特性就会只在稍后版本中可用了。若咱们打算利用上这些新特性,咱们将需要切换版本。

有关更多细节,[版本指南,Edition Guide](https://doc.rust-lang.org/stable/edition-guide/) 是本列举了不同版本间差异,并解释了怎样通过 `cargo fix`,而自动将咱们的代码更新到新版的一本完整的书。

0 comments on commit ecd2e9e

Please sign in to comment.