From 65c7bd8c1850dd2947e5f9492a30c267126839c5 Mon Sep 17 00:00:00 2001 From: "Peng Hailin," Date: Sat, 8 Apr 2023 22:33:35 +0800 Subject: [PATCH] Fixing link id --- src/Ch01_Getting_Started.md | 10 +++++----- src/Ch02_Programming_a_Guessing_Game.md | 20 +++++++++---------- src/Ch03_Common_Programming_Concepts.md | 14 ++++++------- src/Ch04_Understanding_Ownership.md | 14 ++++++------- ...Using_Structs_to_Structure_Related_Data.md | 10 +++++----- src/Ch06_Enums_and_Pattern_Matching.md | 4 ++-- ...ojects_with_Packages_Crates_and_Modules.md | 10 +++++----- src/Ch08_Common_Collections.md | 4 ++-- src/Ch09_Error_Handling.md | 12 +++++------ ...Ch10_Generic_Types_Traits_and_Lifetimes.md | 10 +++++----- src/Ch11_Writing_Automated_Tests.md | 2 +- ...Project_Building_a_Command_Line_Program.md | 2 +- ...anguage_Features_Iterators_and_Closures.md | 4 ++-- src/Ch14_More_about_Cargo_and_Crates-io.md | 2 +- src/Ch15_Smart_Pointers.md | 4 ++-- src/Ch16_Fearless_Concurrency.md | 4 ++-- ...t_Oriented_Programming_Features_of_Rust.md | 14 ++++++------- src/Ch18_Patterns_and_Matching.md | 2 +- src/Ch19_Advanced_Features.md | 8 ++++---- ...ect_Building_a_Multithreaded_Web_Server.md | 2 +- src/Ch21_Appendix.md | 8 ++++---- 21 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/Ch01_Getting_Started.md b/src/Ch01_Getting_Started.md index eca989c..b43c455 100644 --- a/src/Ch01_Getting_Started.md +++ b/src/Ch01_Getting_Started.md @@ -7,7 +7,7 @@ - Rust 的包管理器和构建系统 Cargo 的使用。 -## 安装 +## 安装 第一步即是安装 Rust。这里将通过 `rustup` 这个用于管理 Rust 版本及相关工具的命令行工具,来下载 Rust。要下载 Rust,就需要互联网连接。 @@ -64,7 +64,7 @@ $ rustup update $ rustup self uninstall ``` -## 故障排除 +## 问题排除 要检查当前是否安装了 Rust, 请开启一个 `shell` 并敲入这行命令: @@ -116,7 +116,7 @@ $ cd hello_world > cd hello_world ``` -### 编写及运行 Rust 程序 +### 编写及运行 Rust 程序 接下来,就要构造一个源代码文件,并命名为 `main.rs`。Rust 文件总是以 `.rs` 扩展名结束。若要在文件名中是一多个单词,那么请使用下划线来将这些单词隔开。比如,请使用 `hello_world.rs` 而不是 `helloworld.rs`。 @@ -148,7 +148,7 @@ Hello, World! Hello, World! ``` -而不论所在操作系统为何,字符串 `Hello, World!` 都应打印到终端。而若没有看到这个输出,那么请回到安装小节的 [“故障排除”](#troubleshooting) 部分获取帮助。 +而不论所在操作系统为何,字符串 `Hello, World!` 都应打印到终端。而若没有看到这个输出,那么请回到安装小节的 [“问题排除”](#问题排除) 部分获取帮助。 如确实打印出了 `Hello, World!`,那么恭喜你!你已正式编写除了一个 Rust 程序了。那就让你成为了一名 Rust 程序员了 -- 欢迎! @@ -232,7 +232,7 @@ Cargo 是 Rust 的构建系统和包管理器。由于 Cargo 处理了很多任 对于最简单的那些 Rust 程序,比如才写的那个,是没有任何依赖的。因此若使用 Cargo 来构建这个 `Hello, World!` 项目,那么就只会用到 Cargo 处理代码构建的部分。而随着更为复杂 Rust 程序的编写,就会添加依赖,而在开始一个用到 Cargo 的项目时,完成依赖添加就会容易得多。 -由于广大 Rust 项目都用到了 Cargo,本书其余部分就假定了也使用 Cargo。若使用了在 [安装](#installation) 小节中提到的官方安装器进行的 Rust 安装,那么Cargo就已与 Rust 一起安装好了。而若是以其他方式安装的 Rust,那么就要通过在终端中敲入下面的命令,来检查 Cargo 是否已安装妥当: +由于广大 Rust 项目都用到了 Cargo,本书其余部分就假定了也使用 Cargo。若使用了在 [安装](#安装) 小节中提到的官方安装器进行的 Rust 安装,那么Cargo就已与 Rust 一起安装好了。而若是以其他方式安装的 Rust,那么就要通过在终端中敲入下面的命令,来检查 Cargo 是否已安装妥当: ```console $ cargo --version diff --git a/src/Ch02_Programming_a_Guessing_Game.md b/src/Ch02_Programming_a_Guessing_Game.md index 8677533..798e37b 100644 --- a/src/Ch02_Programming_a_Guessing_Game.md +++ b/src/Ch02_Programming_a_Guessing_Game.md @@ -108,7 +108,7 @@ fn main() { 这段代码在打印提示消息,表明该游戏是什么及正在请求用户输入。 -## 使用变量保存那些值 +## 使用变量保存那些值 接下来,就要创建一个 *变量(variable)* 来存储用户输入,像下面这样: @@ -122,14 +122,14 @@ fn main() { let apples = 5; ``` -这行代码创建了一个新的名为 `apples` 的变量,并将其绑定到了值 `5`。在 Rust 中,默认变量是不可变的(immutable)。在后续第 3 章的 [变量及可变性](Ch03_Common_Programming_Concepts.md#variables-and-mutability) 小节,将对此概念加以讨论。而要让变量可变,就要将变量名字前加上 `mut` 关键字: +这行代码创建了一个新的名为 `apples` 的变量,并将其绑定到了值 `5`。在 Rust 中,默认变量是不可变的(immutable)。在后续第 3 章的 [变量及可变性](Ch03_Common_Programming_Concepts.md#变量及可变性) 小节,将对此概念加以讨论。而要让变量可变,就要将变量名字前加上 `mut` 关键字: ```rust let apples = 5; // 不可变(immutable) let mut bananas = 5; // 可变(mutable) ``` -> 注意:这里的 `//` 语法,开始了一条持续到那个行结束的代码注释。Rust 会忽略注释中的全部内容。在 [第 3 章](Ch03_Common_Programming_Concepts.md#comments) 将更加详细地讨论代码注释。 +> 注意:这里的 `//` 语法,开始了一条持续到那个行结束的代码注释。Rust 会忽略注释中的全部内容。在 [第 3 章](Ch03_Common_Programming_Concepts.md#注释) 将更加详细地讨论代码注释。 回到这个猜数游戏程序,那么此刻就明白了那个 `let mut guess` 将引入一个名为 `guess` 的可变变量。而那个等号(`=`),则是告诉 Rust,现在要将某个东西绑定到该变量了。等号右边就是要绑定到 `guess` 的那个值,而这个值则是调用 `String::new` 的结果,这个 `String::new`,则又是一个返回一个 `String` 实例的函数。`String` 是由标准库提供的一个字符串类型,为一个可增大的、经 UTF-8 位编码的文本(a growable, UTF-8 encoded bit of text)。 @@ -152,7 +152,7 @@ let mut bananas = 5; // 可变(mutable) 其中的 `&` 表明该参数是个 *引用(reference)*,而引用则是一种无需将数据多次拷贝到内存中的情况下,就可以实现代码多个部分对该数据进行读写的特性(注:在 C 家族语言中,`&`表示内存地址,因此 Rust 中的引用,与指针有类似之处)。引用是一项复杂特性,同时 Rust 的主要优点之一,就是安全而便利地运用引用的方式。对于完成这个猜数游戏,是不必对这些细节有过多了解的。现在要明白的是,与变量类似,引用默认也是不可变的。因此,这里就要写上 `&mut guess` 而不是 `&guess`,来令到这个到 `guess` 的引用为可变的。(第 4 章将更详细地对引用进行解释。) -## 处理潜在的带有 `Result` 的程序失效 +## 处理潜在的带有 `Result` 的程序失效 **Handle Potential Failure with the `Result` Type** @@ -170,7 +170,7 @@ io::stdin().read_line(&mut guess).expect("读取输入失败"); 不过这样的一个长代码行,难于阅读,因此最好将其分开为多个断行。在以 `.method_name()` 语法调用方法时,通过引入另起一行及缩进,来将长的代码行拆分为短代码行,通常是明智的。下面就来说说这一行完成了什么。 -前面讲过,`read_line`方法将用户敲入的东西,放入到传递给他的那个字符串中,然而 `read_line` 还会返回一个值 -- 在此实例中,返回的就是一个 `io::Result` 类型值。Rust 在他的标准库中,有着数个名为 `Result` 的类型:这是一个泛型的 `Result`,对于那些子模组都有着特定版本,比如这里的 `io::Result`。`Result` 的那些类型都属于 [枚举(enumerations)](Ch06_Enums_and_Pattern_Matching.md#enums),枚举常被写为 `enums`,枚举有着一套被称作 *变种(variants)* 的可能值。枚举常常是和 `match` 关键字一起使用的,而 `match` 则是一种条件判断,在符合某个条件时,就可以很方便地根据枚举中的哪个变种,来执行不同代码。 +前面讲过,`read_line`方法将用户敲入的东西,放入到传递给他的那个字符串中,然而 `read_line` 还会返回一个值 -- 在此实例中,返回的就是一个 `io::Result` 类型值。Rust 在他的标准库中,有着数个名为 `Result` 的类型:这是一个泛型的 `Result`,对于那些子模组都有着特定版本,比如这里的 `io::Result`。`Result` 的那些类型都属于 [枚举,enumerations](Ch06_Enums_and_Pattern_Matching.md#定义一个枚举),枚举常被写为 `enums`,枚举有着一套被称作 *变种(variants)* 的可能值。枚举常常是和 `match` 关键字一起使用的,而 `match` 则是一种条件判断,在符合某个条件时,就可以很方便地根据枚举中的哪个变种,来执行不同代码。 第 6 章将深入涵盖到枚举数据结构。而这些 `Result` 类型的目的,则是对错误处理信息进行编码。 @@ -240,7 +240,7 @@ $ cargo run  ✔ 此刻,这游戏的第一部分就算完成了:这里正从键盘获取到输入,并随后将输入打印出来。 -## 生成秘密数字 +## 生成秘密数字 接下来,就需要生成一个用户将要试着去猜的秘密数字了。生成的秘密数字应每次都不相同,这样这游戏在多次玩的时候才有趣。为了不让这个游戏太难,这里要用一个 `1` 到 `100` 之间的随机数。Rust 在其标准库中尚未包含随机数功能。不过 Rust 团队还真的提供了一个 [`rand` 代码箱](https://crates.io/crates/rand),这里就姑且把这样的代码箱,称之为功能吧。 @@ -426,7 +426,7 @@ $ cargo run  ✔  就会得到不同的随机数字,并且他们都应是 `1` 到 `100` 之间的数字。非常棒! -## 将猜数与秘数相比较 +## 将猜数与秘数相比较 既然有了用户输入和随机数,就可以加以比较了。比较的步骤在下面的清单 2-4 中给出了。请注意这个代码还不会编译,原因后面会解释。 @@ -514,7 +514,7 @@ let guess: u32 = guess.trim().parse().expect("请输入一个数字!"); 这里将这个新变量,绑定到了表达式 `guess.trim().parse()`。该表达式中的 `guess` 援引的是原来那个包含着字符串形式输入的 `guess`。而作用在 `String` 实例上的 `trim` 方法,将消除开头和结尾的全部空白,必须要进行这个操作,才能将字符串转换到 `u32` 类型,`u32`只能包含数字数据。为了满足到 `read_line` 并输入他们的猜数,用户必须要按下回车键,这样就会将一个换行字符添加到那个字符串。比如在用户敲入了 `5` 然后按下回车键时,`guess`看起来就会是这样:`5\n`。其中的 `\n` 表示 “换行(newline)”。(在 Windows 上,按下回车键会导致一个回车字符和一个换行字符,即 `\r\n`)。这 `trim` 会将 `\n` 或 `\r\n` 消除,而结果就只是 `5` 了。 -[字符串上的 `parse` 方法](https://doc.rust-lang.org/std/primitive.str.html#method.parse) 将只会在那些逻辑上可被转换成数字的字符上运作,而因此就很可能引起错误。比如说在字符串包含了 `A👍%` 时,就没有办法将其转换成一个数字。由于 `parse` 方法会失败,因此他返回的是个 `Result` 类型,这与 `read_line` 方法所做的一样(在早先的 [用 `Result` 类型处理潜在失败](#handling-potential-failure-with-the-result-type) 中讨论过)。这里再次使用 `expect` 方法对这个`Result` 进行了同样的处理。在因为 `parse` 无法从字符串创建出一个数字,而返回了一个 `Err` 的 `Result` 变种时,这个 `expect` 就会令到游戏崩溃,并将给他的那条消息打印出来。而在 `parse` 可成功将那个字符串,转换成数字时,`expect` 就会返回 `Result` 的 `Ok` 变种,同时 `expect` 会返回这里想要的、`Ok` 值中的数字。 +[字符串上的 `parse` 方法](https://doc.rust-lang.org/std/primitive.str.html#method.parse) 将只会在那些逻辑上可被转换成数字的字符上运作,而因此就很可能引起错误。比如说在字符串包含了 `A👍%` 时,就没有办法将其转换成一个数字。由于 `parse` 方法会失败,因此他返回的是个 `Result` 类型,这与 `read_line` 方法所做的一样(在早先的 [用 `Result` 类型处理潜在失败](#处理潜在的带有-result-的程序失效) 中讨论过)。这里再次使用 `expect` 方法对这个`Result` 进行了同样的处理。在因为 `parse` 无法从字符串创建出一个数字,而返回了一个 `Err` 的 `Result` 变种时,这个 `expect` 就会令到游戏崩溃,并将给他的那条消息打印出来。而在 `parse` 可成功将那个字符串,转换成数字时,`expect` 就会返回 `Result` 的 `Ok` 变种,同时 `expect` 会返回这里想要的、`Ok` 值中的数字。 现在来运行一下这个程序! @@ -563,7 +563,7 @@ $ cargo run  101 ✘  可以看到,这里已将自猜数输入提示开始的全部代码,移入到循环中了。请确保循环中的那些代码行,都另外缩进四个空格,然后再次运行这个程序。现在程序将会一直要求另一猜数,这实际上引入了新的问题。好像是用户无法退出了! -用户可一直通过键盘快捷键 `Ctrl-C`,来中断这个程序。不过还是有别的方法,来退出这头贪厌的怪兽,就像在 [将猜数与秘密数字比较](#comparing-the-guess-to-the-secret-number)中对 `parse` 方法讨论中提到的那样:在用户输入了非数字的答案时,程序就会崩溃。这里就利用了那个,来实现用户退出,如下所示: +用户可一直通过键盘快捷键 `Ctrl-C`,来中断这个程序。不过还是有别的方法,来退出这头贪厌的怪兽,就像在 [将猜数与秘密数字比较](#将猜数与秘数相比较)中对 `parse` 方法讨论中提到的那样:在用户输入了非数字的答案时,程序就会崩溃。这里就利用了那个,来实现用户退出,如下所示: ```console $ cargo run @@ -599,7 +599,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 敲入 `quit` 就会退出这游戏,不过正如所注意到的,这样做将就要敲入别的非数字输入。至少可以是这种做法是次优的;这里想要在猜到了正确数字时,游戏也要停止。 -## 猜对后的退出 +## 猜对后的退出 下面就来通过添加一条 `break` 语句,将游戏编程为在用户赢了时退出: diff --git a/src/Ch03_Common_Programming_Concepts.md b/src/Ch03_Common_Programming_Concepts.md index e8f05ce..0da1200 100644 --- a/src/Ch03_Common_Programming_Concepts.md +++ b/src/Ch03_Common_Programming_Concepts.md @@ -85,7 +85,7 @@ x 的值为:6 在使用了 `mut` 关键字时,就被允许将绑定到 `x` 的值从 `5` 修改到 `6` 了。除了防止程序错误之外,还要考虑多种权衡。比如,在使用着大型数据结构时,就地修改其的一个实例,就会比拷贝并返回新近分配的实例要快一些(for example, in cases where you're using large data structures, mutating an instance in place may be faster than copying and returning newly allocated instances)。而对于较小的数据结构,创建其新实例,并以更具函数式编程风格来编写代码,则可能更易于理解,那么由此带来的性能下降,相对所取得的思路清晰,也会是值得的。 -## 常量 +## 常量 与不可变变量类似, *常量(constants)* 是一些绑定到名字且不允许修改的值,但常量与变量之间,有些差异。 @@ -183,11 +183,11 @@ error: could not compile `variables` due to previous error 现在已经完成变量运行机制的探讨,接卸来就要看看这些变量可以有的那些其余数据类型了。 -## 数据类型 +## 数据类型 Rust 的所有值,都属于某种确切的 *数据类型(data type)*,数据类型告诉 Rust 所指定的是何种数据,进而 Rust 才知道该怎样使用那个数据。接下来会看看两个数据类型的子集:标量(scalar)类型与复合(compound)类型。 -请牢记 Rust 是门 *静态类型(statically typed)* 语言,这就意味着在运行时,他必须清楚所有变量的类型。基于值与对变量的使用方式,编译器通常可以推断出希望变量使用何种类型来。在可能有许多中类型的情况下,就如同第 2 章 [将猜数与秘密数字比较](Ch02_Programming_a_Guessing_Game.md#compring-the-guess-to-the-secret-number) 小节中,使用 `parse` 把一个 `String` 转换成数字类型时,就必须添加一个类型注释,如下面这样: +请牢记 Rust 是门 *静态类型(statically typed)* 语言,这就意味着在运行时,他必须清楚所有变量的类型。基于值与对变量的使用方式,编译器通常可以推断出希望变量使用何种类型来。在可能有许多中类型的情况下,就如同第 2 章 [将猜数与秘密数字比较](Ch02_Programming_a_Guessing_Game.md#将猜数与秘数相比较) 小节中,使用 `parse` 把一个 `String` 转换成数字类型时,就必须添加一个类型注释,如下面这样: ```rust let guess: u32 = "42".parse().expect("这不是个数字!"); @@ -219,7 +219,7 @@ fn main() { *标量* 类型,表示单个值。Rust 有着四个主要的标量类型:整数、浮点数、布尔值与字符。这些类型,其他语言也有。下面就深入看看他们在 Rust 中是怎样工作的。 -### 整形 +### 整形 **Integer Types** @@ -261,7 +261,7 @@ fn main() { > 关于 **整数溢出** > -> 比方说有个类型为 `u8` 的、可保存 `0` 到 `255` 之间值的变量。在尝试将该变量修改为超出那个范围的某个值,比如 `256` 时,就会发生 *整型溢出(integer overflow)*,而整型溢出又可导致两种行为之一。在以调试模式进行程序编译时,Rust 就会包含整数溢出的检查,在发生了整数溢出时,就会导致程序进入 *错误(panic)* 状态。对于程序因错误而退出执行这种情况,Rust 使用了 猝死(paniking) 这个词语;在第 9 章中的 [带有 `panic!` 宏的不可恢复性错误](Ch09_Error_Handling.md#unrecoverable-errors-with-panic) 小节,将更深入地讨论到程序因错误而终止运行的情况。 +> 比方说有个类型为 `u8` 的、可保存 `0` 到 `255` 之间值的变量。在尝试将该变量修改为超出那个范围的某个值,比如 `256` 时,就会发生 *整型溢出(integer overflow)*,而整型溢出又可导致两种行为之一。在以调试模式进行程序编译时,Rust 就会包含整数溢出的检查,在发生了整数溢出时,就会导致程序进入 *错误(panic)* 状态。对于程序因错误而退出执行这种情况,Rust 使用了 猝死(paniking) 这个词语;在第 9 章中的 [带有 `panic!` 宏的不可恢复性错误](Ch09_Error_Handling.md#) 小节,将更深入地讨论到程序因错误而终止运行的情况。 > > 在以 `--release` 开关进行发布模式的编译时,Rust 就不会包含对引起程序终止运行的整数溢出的检查。这时若发生了溢出,Rust 就会执行 *二进制补码封装(two's complement wrapping)*。简而言之,对于比那种类型能保存的最大值还要大的值,就会被“回卷(wrap around)”到那种类型所能保存的最小值。以 `u8` 为例,值 `256` 就变成了 `0`,值 `257` 就变成了 `1`,如此等等。这样程序就不会死掉,而那个变量则会有着一个或许不是所期望的值。对整数溢出的回卷行为的依赖,被视为一种错误(Relying on integer overflow's wrapping behavior is considered an error)。 > @@ -365,7 +365,7 @@ fn main() { *复合类型(compound types)* 可将多个值组合成一个类型。Rust 有着两个原生的复合类型:元组与数组(tuples and arrays)。 -### 元组类型 +### 元组类型 元组是将数个不同类型的值,组合成一个复合类型的一般方式。元组是固定长度的:一旦被声明出来,他们的大小就无法扩大或缩小了。 @@ -1172,7 +1172,7 @@ fn main() { 此代码结构,消除了使用 `loop`、`if`、`else`、及 `break` 实现同样结构时,很多不可缺少的嵌套,且此结构更为清晰。在条件保持为真期间,代码就会运行;否则,他将退出循环。 -### 使用 `for` 对集合进行遍历 +### 使用 `for` 对集合进行遍历 可选择使用 `while` 结构,来对集合,诸如数组,的那些元素进行循环。作为示例,下面清单 3-4 中的循环,将打印出数组 `a` 中的各个元素。 diff --git a/src/Ch04_Understanding_Ownership.md b/src/Ch04_Understanding_Ownership.md index 0f3fa1a..5e504d6 100644 --- a/src/Ch04_Understanding_Ownership.md +++ b/src/Ch04_Understanding_Ownership.md @@ -124,7 +124,7 @@ Rust 采取了不同的路线:一旦某个变量超出了作用域,那么该 这种模式对 Rust 代码编写方式有深远影响。在此刻他可能看起来还算简单,但在想要让多个变量,使用早先在内存堆上分配的数据,这种更为复杂情形时,代码行为就会无法被预见到。现在就来探讨一下一些这样的情况。 -### 变量与数据互操作方式之一:迁移(所有权) +### 变量与数据互操作方式之一:迁移(所有权) 在 Rust 中,多个变量可以多种方式,与同一数据进行互操作。来看看下面清单 4-2 中用到整数的示例: @@ -234,7 +234,7 @@ error: could not compile `string_demo` due to previous error; 1 warning emitted 此外,这种做法背后,还隐含着一种语言设计上的取舍:Rust 绝不会自动创建数据的 “深” 拷贝。由此,任何 *自动* 拷贝,都可认为在运行时性能开销上的影响很小(Therefore, any *automatic* copying can be assumed to be inexpensive in terms of runtime performance)。 -### 变量与数据交互方式之二:克隆 +### 变量与数据交互方式之二:克隆 在 *确实* 打算对 `String` 的内存堆数据,而非只是栈数据进行深度拷贝时,就可以使用一个常用的、名为 `clone` 的方法。在第 5 章将讨论到方法语法,而由于在众多编程语言中,方法都是共同特性,那么此前大概率是见到过方法的。 @@ -253,7 +253,7 @@ fn main() { 当看到一个对 `clone` 方法的调用时,那么就明白正有一些任性代码在被执行,且那代码可能开销高昂。对此方法的调用,是某些不寻常事情正在发生的直观指示。 -### 唯栈数据:拷贝(stack-only data: copy) +### 唯栈数据:拷贝(stack-only data: copy) 尚有另一个至今还未讲到的小问题。正使用着整数的这段代码 -- 其中一部分在下面的清单 4-2 中给出了 -- 会工作并是有效代码: @@ -614,7 +614,7 @@ error: could not compile `ownership_demo` due to previous error 虽然这些所有权借用方面的错误,时常令人沮丧,但请记住这正是 Rust 编译器,于早期阶段(在编译时而非运行时)就在指出潜在错误,并表明问题准确所在。代码编写者这才不必去追踪为何数据不是先前所设想的那样。 -### 悬空引用,dangling references +### 悬空引用,dangling references 在有着指针的那些语言中,都容易通过在保留了到某些内存的一个指针同时,释放了那些内存,而错误地创建出 *悬空指针,a dangling pointer* -- 引用了内存中,可能已经给了其他指针的某个地址的一个指针。在 Rust 中,与此相对照,编译器会确保引用绝不会成为悬空引用:在有着到某数据的引用时,编译器会确保在到该数据的引用,超出作用域之前,被引用的数据不超出作用域。 @@ -699,7 +699,7 @@ fn dangle() -> String { 接下来,将看看一种不同类别的引用:切片(slices)。 -## 切片类型(the slice type) +## 切片类型(the slice type) *切片(slices)* 特性,实现了对集合中一个连续元素序列,而非对整个集合的引用。切片是引用的一种类别,因此他不会持有所有权。 @@ -789,7 +789,7 @@ fn second_word(s: &String) -> (usize, usize) { 幸运的是,Rust 有此问题的解决办法,那就是:字符串切片(string slices)。 -### 字符串切片 +### 字符串切片 字符串切片是到某个 `String` 类型值部分的引用,而看起来像下面这样: @@ -921,7 +921,7 @@ let s = "Hello, world!"; 这里的变量 `s` 的类型,即为 `&str`:他是个指向到二进制文件特殊点位的一个切片。这也是为何字符串字面值为不可变的原因;`&str` 类型属于不可变引用。 -### 字符串切片作为函数参数 +### 字符串切片作为函数参数 了解了咱们可在函数中,取字符串字面值的切片及 `String` 值,就引出了对 `first_word` 函数的又一项改进,而下面就是函数 `first_word` 的签名: diff --git a/src/Ch05_Using_Structs_to_Structure_Related_Data.md b/src/Ch05_Using_Structs_to_Structure_Related_Data.md index c076dd3..9c50572 100644 --- a/src/Ch05_Using_Structs_to_Structure_Related_Data.md +++ b/src/Ch05_Using_Structs_to_Structure_Related_Data.md @@ -136,7 +136,7 @@ fn main() { 请注意结构体更新语法,像赋值一样使用了 `=`;这是由于结构体更新语法迁移了数据,就跟在之前的 ["变量与数据互动方式:迁移"](Ch04_Understanding_Ownership.md#ways-variables-and-data-interact-move) 小节中看到的那样。在此示例中,在创建了 `user2` 之后,由于变量 `user1` 中的 `username` 字段中的 `String` 值,已被迁移到 `user2` 中了,因此就再也不能使用变量 `user1` 了。若给到 `user2` 的 `email` 及 `username` 字段都是新的 `String` 值,而因此只使用来自 `user1` 的 `active` 和 `sign_in_count` 值,那么在创建了 `user2` 之后,`user1` 仍将是有效的。因为 `active` 和 `sign_in_count` 的类型,都是实现了 `Copy` 特质的类型,因此就会应用在 [唯栈数据:拷贝](Ch04_Understanding_Ownership.md#stack-only-data-copy) 小节中的行为表现。 -### 使用不带命名字段的元组结构体来创建不同类型 +### 使用不带命名字段的元组结构体来创建不同类型 **Using Tuple Structs without Named Fields to Create Different Types** @@ -349,7 +349,7 @@ fn area(rectangle: &Rectangle) -> u32 { `area` 函数会访问那个 `Rectangle` 实例的 `width` 和 `height` 字段。`area` 的函数签名现在表达的正是这里想要的了:使用 `Rectangle` 的 `width` 和 `height` 字段,计算出他的面积。这就传达出了这里的宽与高是相互关联,同时这样做还给到了这些值描述性的名称,而非使用之前元组的索引 `0` 和 `1` 了。这在代码清晰上得了一分。 -### 使用派生特质加入有用功能 +### 使用派生特质加入有用功能 **Adding Useful Functionality with Derived Traits** @@ -500,7 +500,7 @@ cargo run *方法* 与函数类似:是以 `fn` 关键字和一个名称,来声明出方法,方法可以有参数和返回值,同时包含了在某个地方方法被调用时,运行的一些代码。与函数不同的地方在于,方法是在结构体(或者枚举或特质对象,关于枚举即特质对象,将分别在第 6 和 17 章讲到)的语境里面定义的,且方法的首个参数将始终是 `self`,这个 `self` 表示方法被调用的那个结构体实例本身。 -### 方法的定义 +### 方法的定义 下面就来将那个将一个 `Rectangle` 实例作为参数的 `area` 函数,修改为定义在 `Rectangle` 结构体上的 `area` 方法,如下清单 5-13 所示: @@ -569,8 +569,8 @@ fn main() { 通常,但并非总是这样,在给到方法与某个字段同样名字时,要的是让那个方法返回与其同名字段中的值,而不会去干别的事情。像这样的方法,就叫做 *获取器(getters)*,而 Rust 并未像其他语言所做的那样,自动实现结构体字段的获取器。由于可将字段构造为私有,而将方法构造为公开,而由此实现对作为类型的公开 API一部分的字段的只读访问。在第 7 章中就会讨论到何为公开与私有,以及怎样将字段或方法指定为公开或私有。 -#### `->` 操作符(the `->` operator)哪去了呢? -> +#### `->` 操作符(the `->` operator)哪去了呢? + > 在 C 和 C++ 中,方法调用会用到两个操作符:直接调用在对象上的方法时,要用到 `.`,而在对象的指针上调用方法时,则要用 `->` 操作符,这时还先要对该指针解除引用。换句话说,在 `object` 是个指针时,`object -> something()` 是类似于 `(*object) -> something()` 的。 > Rust 并无 `->` 操作符的等价操作符;相反,Rust 有着一项名为 *自动引用与解引用(automatic referencing and dereferencing)* 的特性。而方法调用就是 Rust 中有着这种行为表现的少数几个地方之一。 > diff --git a/src/Ch06_Enums_and_Pattern_Matching.md b/src/Ch06_Enums_and_Pattern_Matching.md index 258a289..8085eb8 100644 --- a/src/Ch06_Enums_and_Pattern_Matching.md +++ b/src/Ch06_Enums_and_Pattern_Matching.md @@ -24,7 +24,7 @@ enum IpAddrKind { 现在 `IpAddrKind` 就是一个可在代码中别的地方使用的定制数据类型了。 -### 枚举取值 +### 枚举取值 可像下面这样,创建出 `IpAddrKind` 两个变种的实例来: @@ -269,7 +269,7 @@ error: could not compile `enum_demo` due to previous error 总的来说,为了使用某个 `Option` 值,就要有将会处理各个变种的代码。要有一些只会在有着一个 `Some` 的值时运行的代码,而此情况下就会允许这代码使用那个内部的 `T` 类型变量。在有着 `None` 值时,则还要有别的代码来允许了,而这代码就没有可用的 `T` 类型值了。在与枚举一起使用的时候,`match` 表达式正是实现此特性的控制流结构:`match` 表达式将依据枚举有着哪些变种,而运行相应的不同代码,以及哪些代码可使用匹配值内部的数据。 -## `match` 控制流结构 +## `match` 控制流结构 Rust 有值一种即为强大的、名为 `match` 的控制流结构,此控制流结构实现了将某个值与一系列模式的比较,并根据所匹配模式而执行相应的代码。模式可由字面值、变量名字、通配符及其他事物等构成;第 18 章会涵盖到全部不同种类的模式及其所完成的事情。`match` 的强大来自模式的表达能力,以及编译器对全部可能情形都被处理进行确认这一事实。 diff --git a/src/Ch07_Managing_Growing_Projects_with_Packages_Crates_and_Modules.md b/src/Ch07_Managing_Growing_Projects_with_Packages_Crates_and_Modules.md index f71024d..e6fea60 100644 --- a/src/Ch07_Managing_Growing_Projects_with_Packages_Crates_and_Modules.md +++ b/src/Ch07_Managing_Growing_Projects_with_Packages_Crates_and_Modules.md @@ -186,7 +186,7 @@ crate 模组树或许会令人想到计算机上文件系统的目录树;这可是一个极为恰当的类比!就跟文件系统中的目录一样,使用模组是为对代码进行组织。而正如目录中的那些文件,这里需要一种找到那些模组的方法。 -## 用于引用目录树中项目的路径 +## 用于引用目录树中项目的路径 **Paths for Referring to an Item in the Module Tree** @@ -272,7 +272,7 @@ error: could not compile `restaurant` due to 2 previous errors Rust 选择了让模组系统以这种方式发挥作用,从而默认就将内部实现细节给隐藏了。如此一来,就清楚可修改内部代码的哪些部分,而不会破坏外层代码。尽管如此,Rust 还是提供了通过使用 `pub` 关键字,把某个程序项目构造为公共项目,而将子模组代码的内层部分,暴露给外层祖辈模组的选项。 -### 使用 `pub` 关键字对路径进行暴露 +### 使用 `pub` 关键字对路径进行暴露 下面回到清单 7-4 中,告知 `hosting` 模组为私有的那个错误。这里希望在父模组中的 `eat_at_restaurant` 函数,有着到那个 `hosting` 子模组中的 `add_to_waitlist` 函数的访问权限,因此就要将该模组,以 `pub` 关键字标记起来,如下面清单 7-5 中所示。 @@ -561,7 +561,7 @@ error: could not compile `restaurant` due to previous error; 1 warning emitted 请注意这里还有那个 `use` 在其作用域中已不再被使用的一个告警!为修复此问题,就同时要将那个 `use` 语句,移入到那个 `customer` 模组内部,或者在那个子 `customer` 模组内部,以 `super::hosting` 来引用父模组中的那个快捷方式。 -### 创建惯用 `use` 路径 +### 创建惯用 `use` 路径 在上面的清单 7-11 中,你或许会想,为什么那里指定了 `use crate::front_of_house::hosting`,并随后在 `eat_at_restaurant` 函数中调用了 `hosting::add_to_waitlist`,而不是将那个 `use` 路径,指定为一直到那个 `add_to_waitlist` 函数,以达到同样目的,即如下清单 7-13 中那样。 @@ -777,7 +777,7 @@ use std::collections::*; 通常是在测试时,要将正在测试的全部程序项目带入到 `tests` 模组,才使用这个全局操作符;在第 11 章中的 [怎样编写测试](Ch11_Writing_Automated_Tests.md#how-to-write-tests) 小节,就会讲到这个问题。在序曲模式(the prelude pattern)中,有时也会用到全局操作符:请参阅 [标准库文档](https://doc.rust-lang.org/std/prelude/index.html#other-preludes),了解有关更多序曲模式的知识。 -## 将模组拆分为不同文件 +## 将模组拆分为不同文件 **Separating Modules into Different Files** @@ -835,7 +835,7 @@ pub fn add_to_waitlist() {} 相反如果将 `hosting.rs` 放在 `src` 目录,那么编译器就会以为 `hosting.rs` 的代码,是在声明于代码箱根部的 `hosting` 模组中的,而不是那个 `front_of_house` 模组的子模组中的。为了获取模组代码,而要查看那些文件方面的编译器规则,就表明这些目录与文件,甚为紧密地于模组树结构相匹配。 -#### 备用文件路径 +#### 备用文件路径 > > 本小节讲的是 Rust 编译器所用到的最惯用的文件路径;但较早的文件路径仍被支持。 diff --git a/src/Ch08_Common_Collections.md b/src/Ch08_Common_Collections.md index 813e0cb..66b330f 100644 --- a/src/Ch08_Common_Collections.md +++ b/src/Ch08_Common_Collections.md @@ -155,7 +155,7 @@ error: could not compile `vec_demo` due to previous error > **请注意**:更多有关 `Vec` 类型的实现细节,请参考 [Rust 专论(The Rustonomicon)](https://doc.rust-lang.org/nomicon/vec/vec.html)。 -### 对矢量中那些值的迭代 +### 对矢量中那些值的迭代 要依次访问矢量中的各个元素,就要迭代全部元素,而非使用那些索引值,一次访问一个了。下面清单 8-8 展示了怎样使用 `for` 循环,来获取到一个 `i32` 矢量值中各个元素的不可变引用,并将这些元素打印出来。 @@ -325,7 +325,7 @@ Rust 标准库还包含了一些其他字符串类型,比如 `OsString`、`OsS 作为上面代码的结果,`s` 将包含 `lol`。 -#### 使用 `+` 运算符或 `format!` 宏的字符串连接 +#### 使用 `+` 运算符或 `format!` 宏的字符串连接 通常,会想要将两个既有字符串合在一起。完成此操作的一种方式,就是使用 `+` 运算符,如下清单 8-18 中所示。 diff --git a/src/Ch09_Error_Handling.md b/src/Ch09_Error_Handling.md index 96adc2f..03b431c 100644 --- a/src/Ch09_Error_Handling.md +++ b/src/Ch09_Error_Handling.md @@ -114,7 +114,7 @@ note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose bac 在本章的 [要 `panic!` 或不要 `panic!`](#to-panic-or-not-to-panic) 小节,将回到 `panic!` 这个话题,并讨论在何时要用 `panic!`,何时不应使用 `panic!` 来处理不同错误情形。接下来,就会看看怎样使用 `Result`,从错误中恢复过来。 -## 带有 `Result` 的可恢复错误 +## 带有 `Result` 的可恢复错误 多数错误都没有严重到要求程序整个地停止运行。某些时候,在某个函数失败时,必定是由于某种可易于解释进而加以响应的原因。比如在尝试打开某个文件,而因为要打开的文件不存在,那个操作失败了时,那么可能希望创建该文件,而不是中止这个进程。 @@ -270,7 +270,7 @@ fn main() { > 尽管此代码与清单 9-5 有着同样行为,但他并未包含任何的 `match` 表达式,且读起来更清楚。请在读完了第 13 章,并看看标准库文档中的这个 `unwrap_or_else` 方法后,再回到这个示例。在对错误进行处理时,许多别的这些方法,都可以清理掉大量嵌套的 `match` 表达式。 -### 因错误而中止的快捷方式:`unwrap` 与 `expect` +### 因错误而中止的快捷方式:`unwrap` 与 `expect` **Shortcuts for Panic on Error: `unwrap` and `expect`** @@ -362,7 +362,7 @@ fn read_username_from_file() -> Result { 由于在 Rust 中这样的传递错误模式是如此普遍,以致于 Rust 提供了问好操作符(the question mark operator, `?`),来令到错误传递更加容易。 -### 传递错误的快捷方式:`?` 操作符 +### 传递错误的快捷方式:`?` 操作符 下面清单 9-7 给出了与清单 9-6 有着同样功能的一个 `read_username_from_file` 实现,只是此实现使用了 `?` 操作符。 @@ -533,7 +533,7 @@ fn main() -> Result<(), Box> { 而当方法调用在测试中失败时,即使那个方法并非正在测试的某些功能,也会希望整个测试失败。由于 `panic!` 正是将测试标记为失败的方式,那么调用 `unwrap` 或 `expect`,则正是要用到的了。 -### 相比于编译器,代码编写者掌握了更多信息的情形 +### 相比于编译器,代码编写者掌握了更多信息的情形 在有着确保了 `Result` 将有着 `Ok` 值的其他某些逻辑,但编译器对此逻辑却一无所知时,调用 `unwrap` 或 `expect` 也是恰当的。这时将仍然有个需要处理的 `Result` 值:对于不论所调用的什么操作,即使在当前特定情形下,逻辑上失败绝无可能,但所调用的操作原本总体上仍是有失败可能。在经由亲自检查代码,而能确保绝不会有 `Err` 变种时,调用 `unwrap` 就是完美可接受的,且将自己设想的绝不会有 `Err` 变种的原因,在 `expect` 文本中撰写出来,这样做甚至更佳。下面就是一个示例: @@ -550,7 +550,7 @@ fn main () { 这里是在通过解析硬编码字符串,创建一个 `IpAddr`。可以看到 `127.0.0.1` 是个有效的 IP 地址,因此这里使用 `expect` 是可接受的。然而,有着一个硬编码的、有效的字符串,并未改变 `parse` 方法返回值类型:这里仍将得到一个 `Result` 类型,同时由于编译器不是足够聪明到发现这个字符串总是个有效的 IP 地址,那么编译器仍将要求,以 `Err` 变种是一种可能性那样,对这个 `Result` 进行处理。在这个 IP 地址为来自用户输入,而非这里的硬编码到程序中,进而 *确实* 有着失败可能时,无疑就要打算对这个 `Result` 进行更为健壮的处理了。这种提及这个 IP 地址为硬编码的假设,将提醒到在将来,需要从其他来源获取这个 IP 地址时,就要把 `expect` 修改为更好的错误处理代码。 -### 错误处理守则 +### 错误处理守则 在代码可能以糟糕状态结束运行时,那么让代码中止运行就是明智的。在这种情形下,所谓 *糟糕状态(a bad state)* 就是在某种假设、保证、合约,或恒值已被破坏,譬如在无效值、矛盾值,或缺失值被传递到所编写代码 -- 加上以下的一项或多项: @@ -566,7 +566,7 @@ fn main () { 但是,在全部的函数中,进行大量错误检查,则会显得冗长而烦人。幸运的是,可使用 Rust 的类型系统(并因此由编译器完成类型检查),来完成许多的检查。在函数有着作为参数的特定类型时,就可以在知悉编译器已经确保有着有效值的情况下,着手处理代码的业务逻辑。比如,在有着一个不同于 `Option` 的类型时,程序就期望有 *某个东西(something)* 而非 *什么也没有(nothing)*。代码这时就不必处理 `Some` 与 `None` 变种的两种情形:无疑将只有一种有着某个值的情形。尝试将无值传递给该函数的代码,甚至都不会编译,那么该函数就不必在运行时对那样的情况进行检查了。另一个示例则是使用某个诸如 `u32` 无符号整数,这就确保了参数绝不会是个负数。 -### 创建用于验证的定制类型 +### 创建用于验证的定制类型 接下来将这个运用 Rust 的类型系统,来确保有着有效值的概念,进行进一步拓展,而看看创建一个用于验证的定制类型。回顾在第二章中的猜数游戏,其中的代码要求用户猜出一个 `1` 与 `100` 之间的数字。在将用户猜的数字与那里的秘密数字比对之前,是绝无对用户猜数是否处于 `1` 与 `100` 之间,进行过验证的;那里只验证过猜数为正数。在这个示例中,后果并不是非常可怕:这里的输出 “太大了” 或 “太小了” 仍将正确。但引导用户朝向有效的猜数,并在用户猜出不在该范围的数,与用户敲入了比如一些字母时,而有不同的表现,将是一项有用的功能增强。 diff --git a/src/Ch10_Generic_Types_Traits_and_Lifetimes.md b/src/Ch10_Generic_Types_Traits_and_Lifetimes.md index 81cb4b2..6c996ed 100644 --- a/src/Ch10_Generic_Types_Traits_and_Lifetimes.md +++ b/src/Ch10_Generic_Types_Traits_and_Lifetimes.md @@ -436,7 +436,7 @@ fn main() { 此示例的目的,是要对其中有些泛型参数是以 `impl` 来声明,而另一些泛型参数则是以方法定义来声明的一种情形,加以演示。由于这里的泛型参数 `T` 与 `U`与结构体定义在一起,因此他们是在 `impl` 之后声明的。而其中的泛型参数 `X` 与 `Y`,由于他们只与那个方法 `mixup` 有关,因此他们就被声明在了 `fn mixup` 之后。 -### 使用泛型参数代码的性能问题 +### 使用泛型参数代码的性能问题 这里或许想了解,在运用了泛型参数时,是否有着运行时的开销。好消息就是,相比于使用具体类型,使用泛型并不会令到程序运行得更慢。 @@ -477,7 +477,7 @@ fn main() { 这个通用的 `Option`,就被以编译器创建的具体定义给替换掉了。由于 Rust 将通用代码,编译为指明了各个实例中类型的代码,那么就不会为运用泛型,而付出运行时代价。在运行的时候,代码就只是会与原本早先手写的各个重复定义一样执行。单态化的过程,令到 Rust 的泛型特性,在运行时极为高效。 -## 特质:定义共用行为 +## 特质:定义共用行为 *特质(a trait)*,定义了某种特定类型所具有的,并可与其他类型共用的功能。使用特质,就可以抽象方式,来定义共用行为。而使用 *特质边界(trait bounds)*,就可以指明具有特定行为的任意类型的泛型(we can use *trait bounds* to specify that a generic type can be any type that has certain behavior)。 @@ -512,7 +512,7 @@ pub trait Summary { 特质在其代码体中,可以由多个方法:这些方法签名一行一个地列出来,同时每行都已分号结束。 -### 在类型上实现某个特质 +### 在类型上实现某个特质 既然前面已经定义了所需的那个 `Summary` 特质的那些方法的签名,那么就可以将其在此处媒体聚合器中的类型上,加以实现了。下面清单 10-13 就给出了这个 `Summary` 特质,在 `NewsArticle` 结构体上,使用标题、作者以及处所字段来创建 `summaryize` 方法返回值的一种实现。而对于 `Tweet` 结构体,这里是将 `summarize` 定义作在假定推文已被限制为 280 字符情况下,返回用户名加上推文的全部文字。 @@ -662,7 +662,7 @@ impl Summary for Tweet { 请注意从某个方法的重写实现,是无法访问该同一方法的默认实现的。 -### 作为参数的特质 +### 作为参数的特质 既然咱们清楚了怎样定义和实现特质,那么就可以探索一下,怎样使用特质来定义接受不同类型参数的函数了。这里将使用之前清单 10-13 中,定义在 `NewsArticle` 与 `Tweet` 上的那个 `Summary` 特质,来定义一个调用其 `item` 参数上 `summarize` 方法的 `notify` 函数,其中 `item` 参数是某个实现了 `Summary` 特质的类型。要完成这一点,就要使用到 `impl Trait` 语法,如下所示: @@ -852,7 +852,7 @@ let s = 3.to_string(); 特质与特质边界这两个特性,允许咱们编写运用了泛型参数的代码,从而在减少代码重复的同时,还向编译器指出希望该泛型有着特定行为。随后编译器就会使用特质边界信息,来就代码所用到的全部具体类型,是否提供到正确行为进行检查。在一般的动态类型语言(dynamically typed languages)中,若调用某个类型上尚未定义的方法,那么就会在运行时收到错误。但 Rust 将这些错误,移到了编译时,这样就要在代码还不能运行的时候,就被强制要求修复这些问题。此外,由于已在编译时检查过这些问题,那么就不必编写对运行时行为进行检查的代码了。在不必放弃泛型灵活性之下,就提升了程序性能。 -## 使用生命周期对引用加以验证 +## 使用生命周期对引用加以验证 生命周期是另一种前面已经用到的泛型。与确保某种类型有着期望行为的特质不同,生命周期确保的是引用在需要他们有效期间,保持有效(lifetimes ensure that references are valid as long as we need them to be)。 diff --git a/src/Ch11_Writing_Automated_Tests.md b/src/Ch11_Writing_Automated_Tests.md index cfec84b..7a463f5 100644 --- a/src/Ch11_Writing_Automated_Tests.md +++ b/src/Ch11_Writing_Automated_Tests.md @@ -24,7 +24,7 @@ 下面就来看看,Rust 专为编写进行这些操作的测试,而提供到一些特性,包括 `test` 属性(the `test` attribute)、几个宏,以及 `should_panic` 属性(the `should_panic` attribute)。 -### 测试函数剖析 +### 测试函数剖析 **The Anatomy of a Test Function** diff --git a/src/Ch12_An_IO_Project_Building_a_Command_Line_Program.md b/src/Ch12_An_IO_Project_Building_a_Command_Line_Program.md index 69b522b..a0c7cfd 100644 --- a/src/Ch12_An_IO_Project_Building_a_Command_Line_Program.md +++ b/src/Ch12_An_IO_Project_Building_a_Command_Line_Program.md @@ -215,7 +215,7 @@ To an admiring bog! 下面就来通过对这里的项目进行重构,来解决这四个问题。 -### 二进制程序项目的关注点分离 +### 二进制程序项目的关注点分离 **Separation of Concerns for Binary Projects** diff --git a/src/Ch13_Functional_Language_Features_Iterators_and_Closures.md b/src/Ch13_Functional_Language_Features_Iterators_and_Closures.md index 0cc49a4..63f5231 100644 --- a/src/Ch13_Functional_Language_Features_Iterators_and_Closures.md +++ b/src/Ch13_Functional_Language_Features_Iterators_and_Closures.md @@ -184,7 +184,7 @@ error: could not compile `closure-example` due to previous error 这里第一次是以 `String` 值调用的 `example_closure`,编译器便推断出了该闭包 `x` 与返回值的类型为 `String`。这些类型随后便被锁定于 `example_closure` 中的那个闭包里了,而在接下来尝试以不同类型,使用这同样的闭包时,就得到了一个类型错误。 -### 捕获引用抑或迁移所有权 +### 捕获引用抑或迁移所有权 **Capturing Reference or Moving Ownership** @@ -281,7 +281,7 @@ fn main() { 这里生成了一个新线程,将一个闭包作为参数给到这个线程来运行(we spawn a new thread, giving the thread a closure to run as an argument)。其中闭包的函数体,打印出这个清单。在代码清单 13-4 中,由于不可变引用是打印那里清单所需的最少量权限,因此那里的闭包只使用了不可变引用对 `list` 加以了捕获。而在这个示例中,即使其中的闭包函数体只需要不可变引用,这里仍需要通过把那个 `move` 关键字放置于闭包定义的开头,指明那个 `list` 应被迁移到该闭包中。这个新线程可能在主线程其余部分执行完毕之前就执行结束,或主线程可能先结束。若主线程仍保有 `list` 的所有权,而在新线程结束之前就结束,而丢弃掉 `list`,那么在那个线程中的 `list` 就会成为无效。因此,编译器就要求其中的 `list` ,被迁移到那个给到新线程的闭包中,如此那个引用就将有效。请尝试去掉这个 `move` 关键字,或在那个闭包被之后使用 `list`,来看看会得到什么编译器错误! -### 将捕获到的值迁移出闭包与 `Fn` 特质 +### 将捕获到的值迁移出闭包与 `Fn` 特质 **Moving Captured Values Out of Closures and the `Fn` Traits** diff --git a/src/Ch14_More_about_Cargo_and_Crates-io.md b/src/Ch14_More_about_Cargo_and_Crates-io.md index 5942d9e..c534dfc 100644 --- a/src/Ch14_More_about_Cargo_and_Crates-io.md +++ b/src/Ch14_More_about_Cargo_and_Crates-io.md @@ -489,7 +489,7 @@ $ cargo yank --vers 0.1.0 --undo 抽出某个版本,*不会* 删除任何代码。比如,此操作就无法删除那些不小心上传的机密信息。若发生了机密信息被上传的情况,那么就必须立即重置这些机密信息。 -## Cargo 工作区 +## Cargo 工作区 **Cargo Workspaces** diff --git a/src/Ch15_Smart_Pointers.md b/src/Ch15_Smart_Pointers.md index 603b2b0..06d3eb4 100644 --- a/src/Ch15_Smart_Pointers.md +++ b/src/Ch15_Smart_Pointers.md @@ -54,7 +54,7 @@ fn main() { 这里将变量 `b`,定义为有个指向值 `5` 的 `Box` 类型值,值 `5` 被分配在堆上的。此程序将打印出 `b = 5`;在此示例中,即可以与该数据在栈上类似的方式,访问那个匣子中的数据。就跟所有自有值一样(just like any owned value),在某个匣子超出作用域,即 `b` 不处于 `main` 的最后时,他就会被解除内存分配。这种内存解除分配,既发生于这个匣子(存储在栈上的),同时也发生于匣子所指向的数据(存储于内存堆上)。 -### 使用匣子数据结构,实现递归数据类型 +### 使用匣子数据结构,实现递归数据类型 **Enabling Recursive Types with Boxes** @@ -204,7 +204,7 @@ fn main() { `Box` 类型之所以是灵巧指针,是由于他实现了 `Deref` 特质,这就实现了像引用那样,对待 `Box` 类型的值。在某个 `Box` 值超出作用域时,又由于 `Box` 的那个 `Drop` 实现,那么该匣子所指向的内存堆数据就会被清理。相比本章其余部分将讨论到的由其他灵巧指针所提供到的功能,这两个特质甚至将会更为重要。下面就来更深入地探讨一下这两个特质。 -## 在 `Deref` 特质下,像常规引用那样看待灵巧指针 +## 在 `Deref` 特质下,像常规引用那样看待灵巧指针 **Treating Smart Pointers Like Regular References with `Deref` Trait** diff --git a/src/Ch16_Fearless_Concurrency.md b/src/Ch16_Fearless_Concurrency.md index faa6ed7..42bf14b 100644 --- a/src/Ch16_Fearless_Concurrency.md +++ b/src/Ch16_Fearless_Concurrency.md @@ -669,7 +669,7 @@ fn main() { 在弃用了该所之后,咱们就可以打印出该互斥量的值,并看到咱们是能够把那个内层的 `i32`,修改为 `6` 的。 -### 在多个线程间共用 `Mutex` +### 在多个线程间共用 `Mutex` 现在,咱们就来尝试使用 `Mutex`,在多个线程见共用值。咱们将启动 10 个线程,并让他们分别都把一个计数器增加 `1`,因此那个计数器就会从 `0` 到达 `10`。接下来清单 16-13 中的示例,将有着一个编译器报错,同时咱们将使用那个报错,来掌握更多有关使用 `Mutex`,以及 Rust 如何帮助咱们正确运用他的知识。 @@ -870,7 +870,7 @@ fn main() { 咱们将通过讲解 `Send` 与 `Sync` 两个特质,以及怎样与一些定制类型来运用他们来完结本章。 -## `Sync` 与 `Send` 两个特质下的可扩展并发 +## `Sync` 与 `Send` 两个特质下的可扩展并发 **Extensible Concurrency with the `Sync` and `Send` Traits** diff --git a/src/Ch17_Object_Oriented_Programming_Features_of_Rust.md b/src/Ch17_Object_Oriented_Programming_Features_of_Rust.md index 69fc236..e9687ea 100644 --- a/src/Ch17_Object_Oriented_Programming_Features_of_Rust.md +++ b/src/Ch17_Object_Oriented_Programming_Features_of_Rust.md @@ -25,7 +25,7 @@ Erich Gamma、Richard Helm、Ralph Johnson 及 John Vlissides 等的合著 *Desi 运用这个定义,Rust 便是面向对象的:结构体与枚举均有着数据,而 `impl` 块则提供了结构体与枚举上的那些方法。即使有着方法的那些结构体与枚举未*被称作* 对象,根据 The Gang of Four 的对象定义,他们提供了同样的功能。 -### 隐藏了实现细节的封装 +### 隐藏了实现细节的封装 **Encapsulation that Hides Implementation Details** @@ -115,7 +115,7 @@ impl AveragedCollection { 由于这些原因,Rust 便采取了运用特质对象,而非继承的方法。接下来就要看看特质对象是如何实现 Rust 中的多态。 -## 使用允许不同类型值的特质对象 +## 使用允许不同类型值的特质对象 **Using Trait Objects That Allow for Values of Different Types** @@ -135,7 +135,7 @@ impl AveragedCollection { **Defining a Trait for Common Behavior** -为了实现咱们想要 `gui` 所拥有的行为,咱们将定义将有着一个名为 `draw` 方法的名为 `Draw` 特质。随后咱们就可以定义取 *特质对象,a trait object* 的一个矢量。特质对象会同时指向实现了这个指定特质的某个类型,以及用于在运行时查找那个类型上特质方法的一张表。咱们是通过指定某种指针,比如某个 `&` 的引用,或某个 `Box` 的灵巧指针,接着便是 `dyn` 关键字,以及随后指明相关特质,创建出特质对象。(在第 19 章的 [“动态大小类型与 `Sized` 特质”](Ch19_Advanced_Features.md#dynamically-sized-types-and-the-sized-trait) 小节咱们将讲到特质对象必须使用指针的原因。)在泛型或具体类型处,咱们就可以使用特质对象。而不论在何处使用特质对象,Rust 的类型系统都会确保在编译时,在那样的上下文中的任何值,都将实现该特质对象的特质。于是,咱们就无需掌握编译时的所有可能类型了。 +为了实现咱们想要 `gui` 所拥有的行为,咱们将定义将有着一个名为 `draw` 方法的名为 `Draw` 特质。随后咱们就可以定义取 *特质对象,a trait object* 的一个矢量。特质对象会同时指向实现了这个指定特质的某个类型,以及用于在运行时查找那个类型上特质方法的一张表。咱们是通过指定某种指针,比如某个 `&` 的引用,或某个 `Box` 的灵巧指针,接着便是 `dyn` 关键字,以及随后指明相关特质,创建出特质对象。(在第 19 章的 [“动态大小类型与 `Sized` 特质”](Ch19_Advanced_Features.md#动态大小的类型与-sized-特质) 小节咱们将讲到特质对象必须使用指针的原因。)在泛型或具体类型处,咱们就可以使用特质对象。而不论在何处使用特质对象,Rust 的类型系统都会确保在编译时,在那样的上下文中的任何值,都将实现该特质对象的特质。于是,咱们就无需掌握编译时的所有可能类型了。 咱们已经提到过,在 Rust 中,咱们避免将结构体与枚举称为 “对象”,是为了将二者与其他语言中的对象区别开来。在结构体或枚举中,结构体字段中的数据,与 `impl` 代码块中的行为是分开的,而在其他语言中,数据与行为被结合为通常被标称为对象的这么一个概念。然而,特质对象由于其结合了数据与行为,而 *真的* 更像其他语言中的对象。但从无法添加数据到特质对象上看,特质对象是不同于传统的对象的。特质对象并不如其他语言中的对象那样普遍的有用:其特定用途为实现共用行为的抽象。 @@ -340,11 +340,11 @@ error: could not compile `simple_gui` due to previous error 此报错让咱们明白,要么咱们传递给 `Screen` 了某个不是咱们想要传递的东西,那么就应传递另一个类型,要么咱们应在 `String` 上实现 `Draw`,从而 `Screen` 便可以调用其上的 `draw` 方法。 -### 特质对象执行动态调遣 +### 特质对象执行动态调遣 **Trait Object Perform Dynamic Dispatch** -回顾第 10 章中 [“运用了泛型的代码性能问题”](Ch10_Generic_Types_Traits_and_Lifetimes.md#performance-of-code-using-generics) 小节中,在泛型之上运用特质边界时,咱们关于由编译器所完成的单一化过程,the monomorphization process 的讨论:编译器会为咱们在泛型参数处,用到的各个具体类型,而产生出非通用的函数及方法实现。单一化过程所产生的代码,便是在进行 *静态调遣,static dispatch*,这是编译器清楚,咱们在编译时调用的为哪个方法时的情况。这与 *动态调遣,dynamic dispatch* 是相反的,动态调遣是编译器在编译时,无法区分出咱们所调用的为何方法时的情况。在动态调遣情况下,编译器产生出将在运行时,得出要调用方法的代码。 +回顾第 10 章中 [“运用了泛型的代码性能问题”](Ch10_Generic_Types_Traits_and_Lifetimes.md#使用泛型参数代码的性能问题) 小节中,在泛型之上运用特质边界时,咱们关于由编译器所完成的单一化过程,the monomorphization process 的讨论:编译器会为咱们在泛型参数处,用到的各个具体类型,而产生出非通用的函数及方法实现。单一化过程所产生的代码,便是在进行 *静态调遣,static dispatch*,这是编译器清楚,咱们在编译时调用的为哪个方法时的情况。这与 *动态调遣,dynamic dispatch* 是相反的,动态调遣是编译器在编译时,无法区分出咱们所调用的为何方法时的情况。在动态调遣情况下,编译器产生出将在运行时,得出要调用方法的代码。 在咱们运用特质对象时,Rust 就必须使用动态调遣。对于全部可能与用到特质对象代码一起使用的类型,编译器并无掌握,因此他就不明白要调用何种类型上的哪个方法。相反,在运行时,Rust 会使用特质对象内部的指针,来掌握要调用哪个方法。这种做法会导致静态调遣下所不会发生的运行时开销。动态调遣还会阻止编译器内联某个方法代码的抉择,这就相应地阻止了一些优化。然而,咱们却真切地获得了,如同咱们在清单 17-5 中所编写的代码那样的灵活性,同时才能够支持清单 17-9 中那样的情况,如此其便是一种需要考量的取舍了。when we use trait objects, Rust must use dynamic dispatch. The compiler doesn't know all the types that might be used with the code that's using trait objects, so it doesn't know which method implemented on which type to call. Instead, at runtime, Rust uses the pointers inside the trait object to know which method to call. This lookup incurs a runtime cost that doesn't occur with static dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a method's code, which in turn prevents some optimizations. However, we did get extra flexibility in the code that we wrote in Listing 17-5 and were able to support in Listing 17-9, so it's a trade-off to consider. @@ -632,7 +632,7 @@ impl Post { 由于咱们要的是到 `Option` 内部值的一个引用,而非该值的所有权,因此咱们调用了 `Option` 上的 `as_ref` 方法。由于 `state` 是个 `Option>`,在咱们调用 `as_ref` 时,就会返回一个 `Option<&Box>`。而若咱们没有调用 `as_ref`,那么由于咱们无法无法把 `state` 迁移出那个借用的函数参数 `&self`,而将得到一个报错。 -咱们随后调用了 `unwrap` 方法(标准库 `Option` 类型上的),由于咱们清楚,`Post` 上的那些方法,会确保 `state` 将在这些方法完成时,始终包含某个 `Some` 值,因此咱们就明白,这个`unwrap` 是绝不会终止运行的。这便是第 9 章 [咱们相比与编译器掌握着更多信息的情形](Ch09_Error_Handling.md#cases-in-which-you-have-more-information-than-the-compiler) 小节所讲到的情形之一:即咱们明白某个 `Option` 不可能是个 `None` 值,尽管编译器无法掌握这一点。 +咱们随后调用了 `unwrap` 方法(标准库 `Option` 类型上的),由于咱们清楚,`Post` 上的那些方法,会确保 `state` 将在这些方法完成时,始终包含某个 `Some` 值,因此咱们就明白,这个`unwrap` 是绝不会终止运行的。这便是第 9 章 [相比与编译器咱们掌握着更多信息的情形](Ch09_Error_Handling.md#相比于编译器代码编写者掌握了更多信息的情形) 小节所讲到的情形之一:即咱们明白某个 `Option` 不可能是个 `None` 值,尽管编译器无法掌握这一点。 到 `unwrap` 方法这里,当咱们在 `&Box` 上调用 `content` 方法时,强制解引用转换,deref coercion 就会在那个 `&` 及 `Box` 上发挥作用,从而 `content` 方法就将在实现了 `State` 特质的类型上,最终被调用到。而那就意味着咱们需要把 `content` 添加到 `State` 特质的定义,而那正是咱们把根据咱们所有的状态,返回什么样的内容,这种逻辑要放入的地方,如下清单 17-18 中所示: @@ -725,7 +725,7 @@ error: could not compile `simple_blog` due to previous error 至于另一个缺点,便是咱们重复了一些逻辑。为消除一些重复,咱们就可能会尝试构造 `State` 特质上,返回 `self` 的 `request_review` 于 `approve` 两个方法的默认实现;然而,由于该特质不清楚那个具体的 `self` 将为何物,因此这会违反对象安全性,violate object safety。咱们希望能够将 `State` 作为特质对象使用,因此咱们就需要他的那些方法是对象安全的。 -其他代码重复包括了 `Post` 上 `request_review` 与 `approve` 两个方法的一些相似实现。这两个方法都委托给了那个 `Option` 的 `state` 字段中值上的同一方法,并将 `state` 字段的值,设置到方法的结果。若咱们在 `Post` 上有着大量的遵循这种模式的方法,咱们就会考虑定义出一个宏,defining a macro,来消除这种重复(请参阅第 19 章中 ["宏,Macros"](Ch19_Advanced_Features.md#macros) 小节)。 +其他代码重复包括了 `Post` 上 `request_review` 与 `approve` 两个方法的一些相似实现。这两个方法都委托给了那个 `Option` 的 `state` 字段中值上的同一方法,并将 `state` 字段的值,设置到方法的结果。若咱们在 `Post` 上有着大量的遵循这种模式的方法,咱们就会考虑定义出一个宏,defining a macro,来消除这种重复(请参阅第 19 章中 ["宏,Macros"](Ch19_Advanced_Features.md#关于宏) 小节)。 经由这种完全按照面向对象模式下所定义的状态模式,来实现这种模式,咱们就没有利用上原本所能利用的 Rust 的全部优势。下面就来看看,咱们可对那个 `simple_blog` 能做出的,可将无效状态与无效状态转换,构造为编译时错误的一些改变。 diff --git a/src/Ch18_Patterns_and_Matching.md b/src/Ch18_Patterns_and_Matching.md index 2722b02..8ed54e3 100644 --- a/src/Ch18_Patterns_and_Matching.md +++ b/src/Ch18_Patterns_and_Matching.md @@ -872,7 +872,7 @@ error: could not compile `pattern_syntax_demo` due to previous error Rust 不可能确定出在以 `second` 匹配某个值之前,元组中有多少个值要忽略,并随后在那之后又有多少个值要忽略。此代码可能是指咱们打算忽略 `2`,将 `second` 绑定到 `4`,并随后忽略 `8`、`16` 及 `32`;或是指咱们打算忽略 `2` 与 `4`,将 `second` 绑定到 `8`,并随后忽略 `16` 与 `32`;如此等等。名为 `second` 的变量,对于 Rust 并不表示任何特殊的东西,从而由于在两处使用 `..` 属于模棱两可的,因此咱们就收到一个编译报错。 -### 使用匹配卫兵的额外条件,Extra Conditionals with Match Guards +### 使用匹配卫兵的额外条件,Extra Conditionals with Match Guards 所谓 *匹配卫兵,match guard*,是于 `match` 支臂之后被指定出来,对于这条支臂要被选中,而也必须匹配的一个额外 `if` 条件。对于表达相对于所允许的单独模式,更为复杂的一些概念,这样的匹配卫兵就是有用的。 diff --git a/src/Ch19_Advanced_Features.md b/src/Ch19_Advanced_Features.md index 98425a3..665fe39 100644 --- a/src/Ch19_Advanced_Features.md +++ b/src/Ch19_Advanced_Features.md @@ -455,7 +455,7 @@ fn main() {} 运用 `unsafe` 来采取上述五种做法(超能力)没有什么过错,或者不受欢迎。但由于编译器无法助力于保持内存安全,因此要让 `unsafe` 代码正确就更为棘手一些。在有使用 `unsafe` 代码的某种理由时,就可以这样做,而在问题出现时,显式的 `unsafe` 注解,就会令到排查问题原因更为容易。 -## 高级特质 +## 高级特质 在第 10 章 [“特质:定义共用行为”](Ch10_Generic_Types_Traits_and_Lifetimes.md#trait-defining-shared-behavior) 小节中,咱们曾首先涉及到特质,但咱们不曾讨论更为高级的那些细节。现在咱们对 Rust 有了更多了解,咱们就可以深入本质,get into the nitty-gritty。 @@ -892,7 +892,7 @@ impl fmt::Display for Point { 随后在 `Point` 上实现 `OutlinePrint` 就将成功编译,而咱们就可以在 `Point` 实例上调用 `outline_print` 来将其实现在星号轮廓里了。 -### 使用新型模式在外层类型上实现外层的特质 +### 使用新型模式在外层类型上实现外层的特质 **Using the Newtype Pattern to Implement External Traits on External Types** @@ -951,7 +951,7 @@ Rust 的类型系统有着一些到目前为止咱们曾提到过但尚未讨论 新类型还可以隐藏内部实现。比如,咱们可提供一个 `People` 类型,来封装一个存储着某人与其名字关联的 ID 的 `HashMap`。使用 `People` 的代码,只需与咱们提供的公开 API,比如某个将名字字符串添加到 `People` 集合的方法交互;那些代码将不需要知悉咱们在内部分配了`i32` 的 ID 给那些名字。新型模式是达成,咱们曾在第 17 章讨论过的 [“隐藏实现细节的封装”](Ch17_Object_Oriented_Programming_Features_of_Rust.md#encapsulation-that-hides-implementation-details") 的一种轻量方式。 -### 使用类型别名创建类型同义词 +### 使用类型别名创建类型同义词 **Creating Type Synonyms with Type Aliases** @@ -1341,7 +1341,7 @@ fn returns_closure() -> Box i32> { 宏与函数的另一重要区别,便是咱们必须于某个文件中调用宏 *之前*,定义好他们或将他们带入到作用域中,这一点与可在任何地方定义并在任何地方调用的函数相反。 -### 用于通用元编程的带有 `macro_rules!` 的声明式宏 +### 用于通用元编程的带有 `macro_rules!` 的声明式宏 **Declarative Macros with `macro_rules!` for General Metaprogramming** diff --git a/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md b/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md index 7da05b6..b73b680 100644 --- a/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md +++ b/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md @@ -570,7 +570,7 @@ fn main() { 如同咱们在第 16 章中所学到的,`thread::spawn` 讲创建出一个新线程,并于随后在新线程中,运行那个闭包中的代码。当咱们运行此代码,并在浏览器中加载 `/sleep`,随后在另外两个浏览器 Tab 页中加载 `/`,咱们就会看到到 `/` 的请求就不必等待 `/sleep` 请求完毕了。不过,如同咱们曾提到过的,因为咱们正不带任何限制地构造新线程,而最终将使系统不堪重负。 -#### 创建有限数目的线程 +#### 创建有限数目的线程 **Creating a Finite Number of Threads** diff --git a/src/Ch21_Appendix.md b/src/Ch21_Appendix.md index 861e602..e3a7112 100644 --- a/src/Ch21_Appendix.md +++ b/src/Ch21_Appendix.md @@ -5,7 +5,7 @@ ## 附录 A:关键字 -以下清单包含了 Rust 语言当前或今后要用到的一些关键字。由此,他们便不能被用作标识符(除在 [“原始标识符”](#raw-identifiers) 小节中咱们将讨论的那些外)了。所谓标识符,是函数、变量、参数、结构体字段、模组、代码箱、常量、宏、静态值、属性、类型、特质或生命周期等的名字。 +以下清单包含了 Rust 语言当前或今后要用到的一些关键字。由此,他们便不能被用作标识符(除在 [“原始标识符”](#原始标识符) 小节中咱们将讨论的那些外)了。所谓标识符,是函数、变量、参数、结构体字段、模组、代码箱、常量、宏、静态值、属性、类型、特质或生命周期等的名字。 ### 当前在用的关键字 @@ -19,7 +19,7 @@ - `const` - 定义出常量项目或常量原始指针; - `continue` - 继续下一循环迭代; - `crate` - 在模组路径中,指向代码箱根; -- `dyn` - 动态调遣到某个特质对象,参考 [特质对象执行动态调遣](Ch17_Object_Oriented_Programming_Features_of_Rust.md#trait-object-perform-dynamic-dispatch); +- `dyn` - 动态调遣到某个特质对象,参考 [特质对象执行动态调遣](Ch17_Object_Oriented_Programming_Features_of_Rust.md#特质对象执行动态调遣); - `else` - `if` 的回退,及 `if let` 控制流的构件; - `extern` - 链接外部函数或变量; - `false` - 布尔值假的字面值; @@ -69,7 +69,7 @@ - `virtual` - `yield` -### 原始标识符 +### 原始标识符 *原始标识符,raw identifiers* 属于允许实现使用一般不被允许关键字的语法。是通过在关键字前加上前缀 `r#`,使用原始标识符的。 @@ -330,7 +330,7 @@ fn main() { 不能派生的一个特质示例便是 `Display`,其为终端用户处理格式化。咱们应始终要考虑将某个类型显示给用户的恰当方式。终端用户应被允许看到该类型的哪些部分?他们会发现哪些部分是相关的?数据的何种形式才是与他们最为密切相关的?Rust 编译器并无这种见解,因此他就无法为咱们提供到恰当的默认行为。 -这个附录中所提供到的派生特质清单并不详尽:库可以为他们自己的特质实现 `derive`,从而领导咱们可使用 `derive` 的特质清单为真正开放的。实现 `derive` 设计到使用程序性宏,这在第 19 张的 [“关于宏”](Ch19_Advanced_Features.md#关于宏) 小节讲到过。 +这个附录中所提供到的派生特质清单并不详尽:库可以为他们自己的特质实现 `derive`,从而领导咱们可使用 `derive` 的特质清单为真正开放的。实现 `derive` 设计到使用程序性宏,这在第 19 章的 [“关于宏”](Ch19_Advanced_Features.md#关于宏) 小节讲到过。 ### 输出给编程者的 `Debug`