Skip to content

Commit

Permalink
Merge pull request #737 from LiuYinCarl/main
Browse files Browse the repository at this point in the history
修正13到16章部分语法问题
  • Loading branch information
KaiserY authored Sep 29, 2023
2 parents cb2fdf4 + 2ad4177 commit a6a8134
Show file tree
Hide file tree
Showing 13 changed files with 23 additions and 23 deletions.
10 changes: 5 additions & 5 deletions src/ch13-04-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)

结果迭代器版本还要稍微快一点!这里我们将不会查看性能测试的代码,我们的目的并不是为了证明它们是完全等同的,而是得出一个怎样比较这两种实现方式性能的基本思路。

对于一个更全面的性能测试,将会检查不同长度的文本、不同的搜索单词、不同长度的单词和所有其他的可变情况。这里所要表达的是:迭代器,作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能代码。迭代器是 Rust 的 **零成本抽象***zero-cost abstractions*)之一,它意味着抽象并不会引入运行时开销,它与本贾尼·斯特劳斯特卢普(C++ 的设计和实现者)在 “Foundations of C++”(2012)中所定义的 **零开销***zero-overhead*)如出一辙:
对于一个更全面的性能测试,将会检查不同长度的文本、不同的搜索单词、不同长度的单词和所有其他的可变情况。这里所要表达的是:迭代器,作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能的代码。迭代器是 Rust 的 **零成本抽象***zero-cost abstractions*)之一,它意味着抽象并不会引入运行时开销,它与本贾尼·斯特劳斯特卢普(C++ 的设计和实现者)在 “Foundations of C++”(2012)中所定义的 **零开销***zero-overhead*)如出一辙:

> In general, C++ implementations obey the zero-overhead principle: What you don't use, you don't pay for. And further: What you do use, you couldn't hand code any better.
>
Expand All @@ -25,7 +25,7 @@ test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
>
> - 本贾尼·斯特劳斯特卢普 "Foundations of C++"
作为另一个例子,这里有一些取自于音频解码器的代码。解码算法使用线性预测数学运算(linear prediction mathematical operation)来根据之前样本的线性函数预测将来的值。这些代码使用迭代器链来对作用域中的三个变量进行了某种数学计算:一个叫 `buffer` 的数据 slice、一个有 12 个元素的数组 `coefficients`、和一个代表位移位数的 `qlp_shift`。例子中声明了这些变量但并没有提供任何值;虽然这些代码在其上下文之外没有什么意义,不过仍是一个简明的现实中的例子,来展示 Rust 如何将高级概念转换为底层代码:
作为另一个例子,这里有一些取自于音频解码器的代码。解码算法使用线性预测数学运算(linear prediction mathematical operation)来根据之前样本的线性函数预测将来的值。这些代码使用迭代器链对作用域中的三个变量进行某种数学计算:一个叫 `buffer` 的数据 slice、一个有 12 个元素的数组 `coefficients`、和一个代表位移位数的 `qlp_shift`。例子中声明了这些变量但并没有提供任何值;虽然这些代码在其上下文之外没有什么意义,不过仍是一个简明的现实中的例子,来展示 Rust 如何将高级概念转换为底层代码:

```rust,ignore
let buffer: &mut [i32];
Expand All @@ -44,12 +44,12 @@ for i in 12..buffer.len() {

为了计算 `prediction` 的值,这些代码遍历了 `coefficients` 中的 12 个值,使用 `zip` 方法将系数与 `buffer` 的前 12 个值组合在一起。接着将每一对值相乘,再将所有结果相加,然后将总和右移 `qlp_shift` 位。

像音频解码器这样的程序通常最看重计算的性能。这里,我们创建了一个迭代器,使用了两个适配器,接着消费了其值。那么这段 Rust 代码将会被编译为什么样的汇编代码呢?好吧,在编写本书的这个时候,它被编译成与手写的相同的汇编代码。遍历 `coefficients` 的值完全用不到循环:Rust 知道这里会迭代 12 次,所以它“展开”(unroll)了循环。展开是一种移除循环控制代码的开销,并将循环迭代转换为程序中的重复的代码的优化
像音频解码器这样的程序通常最看重计算的性能。这里,我们创建了一个迭代器,使用了两个适配器,接着消费了其值。那么这段 Rust 代码将会被编译为什么样的汇编代码呢?好吧,在编写本书的这个时候,它被编译成与手写的相同的汇编代码。遍历 `coefficients` 的值完全用不到循环:Rust 知道这里会迭代 12 次,所以它“展开”(unroll)了循环。展开是一种将循环迭代转换为重复代码,并移除循环控制代码开销的代码优化技术

所有的系数都被储存在了寄存器中,这意味着访问它们非常快。这里也没有运行时数组访问边界检查。所有这些 Rust 能够提供的优化使得结果代码极为高效。现在知道这些了,请放心大胆的使用迭代器和闭包吧!它们使得代码看起来更高级,但并不为此引入运行时性能损失。

## 总结

闭包和迭代器是 Rust 受函数式编程语言观念所启发的功能。它们对 Rust 以底层的性能来明确的表达高级概念的能力有很大贡献。闭包和迭代器的实现达到了不影响运行时性能的程度。这正是 Rust 竭力提供零成本抽象的目标的一部分。
闭包和迭代器是 Rust 受函数式编程语言观念所启发的功能。它们对 Rust 以高性能来明确的表达高级概念的能力有很大贡献。闭包和迭代器的实现达到了不影响运行时性能的程度。这正是 Rust 竭力提供零成本抽象的目标的一部分。

现在我们改进了我们 I/O 项目的(代码)表现力,让我们看一看更多 `cargo` 的功能,它们将帮助我们准备好将项目分享给世界
现在我们改进了 I/O 项目的(代码)表现力,那么让我们来看看 `cargo` 的更多功能,这些功能将帮助我们将项目分享给世界
4 changes: 2 additions & 2 deletions src/ch14-01-release-profiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
> <br>
> commit 44e31f9f304e0cd9ace01045d17a2aa01a449528
在 Rust 中 **发布配置***release profiles*是预定义的、可定制的带有不同选项的配置,它们允许程序员更灵活地控制代码编译的多种选项。每一个配置都彼此相互独立
在 Rust 中 **发布配置***release profiles*文件是预定义和可定制的,它们包含不同的配置,允许程序员更灵活地控制代码编译的多种选项。每一个配置都相互独立

Cargo 有两个主要的配置:运行 `cargo build` 时采用的 `dev` 配置和运行 `cargo build --release``release` 配置。`dev` 配置被定义为开发时的好的默认配置`release` 配置则有着良好的发布构建的默认配置
Cargo 有两个主要的配置:运行 `cargo build` 时采用的 `dev` 配置和运行 `cargo build --release``release` 配置。`dev` 配置为开发定义了良好的默认配置`release` 配置则为发布构建定义了良好的默认配置

这些配置名称可能很眼熟,因为它们出现在构建的输出中:

Expand Down
4 changes: 2 additions & 2 deletions src/ch14-02-publishing-to-crates-io.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Rust 和 Cargo 有一些帮助他人更方便地找到和使用你发布的包

### 编写有用的文档注释

准确的包文档有助于其他用户理解如何以及何时使用它们,所以花一些时间编写文档是值得的。第三章中我们讨论了如何使用两斜杠 `//` 注释 Rust 代码。Rust 也有特定的用于文档的注释类型,通常被称为 **文档注释**_documentation comments_),它们会生成 HTML 文档。这些 HTML 展示公有 API 文档注释的内容,它们意在让对库感兴趣的程序员理解如何 **使用** 这个 crate,而不是它是如何被 **实现** 的。
准确的包文档有助于其他用户理解如何以及何时使用它们,所以花一些时间编写文档是值得的。第三章中我们讨论了如何使用双斜杠 `//` 注释 Rust 代码。Rust 也有特定的用于文档的注释类型,通常被称为 **文档注释**_documentation comments_),它们会生成 HTML 文档。这些 HTML 展示公有 API 文档注释的内容,它们意在让对库感兴趣的程序员理解如何 **使用** 这个 crate,而不是它是如何被 **实现** 的。

文档注释使用三斜杠 `///` 而不是两斜杆以支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。示例 14-1 展示了一个 `my_crate` crate 中 `add_one` 函数的文档注释,
文档注释使用三斜杠 `///` 而不是双斜杠以支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。示例 14-1 展示了一个 `my_crate` crate 中 `add_one` 函数的文档注释,

<span class="filename">文件名:src/lib.rs</span>

Expand Down
2 changes: 1 addition & 1 deletion src/ch14-03-cargo-workspaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ $ cargo new adder
└── target
```

工作空间在顶级目录有一个 *target* 目录;`adder` 并没有自己的 *target* 目录。即使进入 *adder* 目录运行 `cargo build`,构建结果也位于 *add/target* 而不是 *add/adder/target*。工作空间中的 crate 之间相互依赖。如果每个 crate 有其自己的 *target* 目录,为了在自己的 *target* 目录中生成构建结果,工作空间中的每一个 crate 都不得不相互重新编译其他 crate。通过共享一个 *target* 目录,工作空间可以避免其他 crate 多余的重复构建
工作空间在顶级目录有一个 *target* 目录;`adder` 并没有自己的 *target* 目录。即使进入 *adder* 目录运行 `cargo build`,构建结果也位于 *add/target* 而不是 *add/adder/target*。工作空间中的 crate 之间相互依赖。如果每个 crate 有其自己的 *target* 目录,为了在自己的 *target* 目录中生成构建结果,工作空间中的每一个 crate 都不得不相互重新编译其他 crate。通过共享一个 *target* 目录,工作空间可以避免其他 crate 重复构建

### 在工作空间中创建第二个包

Expand Down
4 changes: 2 additions & 2 deletions src/ch14-04-installing-binaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
> <br>
> commit 704c51eec2f26a0133ae17a2c01986590c05a045
`cargo install` 命令用于在本地安装和使用二进制 crate。它并不打算替换系统中的包;它意在作为一个方便 Rust 开发者们安装其他人已经在 [crates.io](https://crates.io/)<!-- ignore --> 上共享的工具的手段。只有拥有二进制目标文件的包能够被安装。**二进制目标** 文件是在 crate 有 *src/main.rs* 或者其他指定为二进制文件时所创建的可执行程序,这不同于自身不能执行但适合包含在其他程序中的库目标文件。通常 crate 的 *README* 文件中有该 crate 是库、二进制目标还是两者都是的信息
`cargo install` 命令用于在本地安装和使用二进制 crate。它并不打算替换系统中的包;它意在作为一个方便 Rust 开发者们安装其他人已经在 [crates.io](https://crates.io/)<!-- ignore --> 上共享的工具的手段。只有拥有二进制目标文件的包能够被安装。**二进制目标** 文件是在 crate 有 *src/main.rs* 或者其他指定为二进制文件时所创建的可执行程序,这不同于自身不能执行但适合包含在其他程序中的库目标文件。通常 crate 的 *README* 文件中有该 crate 是库、二进制目标还是两者兼有的信息

所有来自 `cargo install` 的二进制文件都安装到 Rust 安装根目录的 *bin* 文件夹中。如果你使用 *rustup.rs* 安装的 Rust 且没有自定义任何配置,这将是 `$HOME/.cargo/bin`。确保将这个目录添加到 `$PATH` 环境变量中就能够运行通过 `cargo install` 安装的程序了。
所有来自 `cargo install` 的二进制文件都安装到 Rust 安装根目录的 *bin* 文件夹中。如果你是使用 *rustup.rs* 来安装 Rust 且没有自定义任何配置,这个目录将是 `$HOME/.cargo/bin`。确保将这个目录添加到 `$PATH` 环境变量中就能够运行通过 `cargo install` 安装的程序了。

例如,第十二章提到的叫做 `ripgrep` 的用于搜索文件的 `grep` 的 Rust 实现。为了安装 `ripgrep` 运行如下:

Expand Down
2 changes: 1 addition & 1 deletion src/ch14-05-extending-cargo.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ Cargo 的设计使得开发者可以通过新的子命令来对 Cargo 进行扩

## 总结

通过 Cargo 和 [crates.io](https://crates.io/)<!-- ignore --> 来分享代码是使得 Rust 生态环境可以用于许多不同的任务的重要组成部分。Rust 的标准库是小而稳定的,不过 crate 易于分享和使用,并采用一个不同语言自身的时间线来提供改进。不要羞于在 [crates.io](https://crates.io/)<!-- ignore --> 上共享对你有用的代码因为它很有可能对别人也很有用!
通过 Cargo 和 [crates.io](https://crates.io/)<!-- ignore --> 来分享代码是使得 Rust 生态环境可以用于许多不同的任务的重要组成部分。Rust 的标准库是小而稳定的,不过 crate 易于分享和使用,并采用一个不同语言自身的时间线来提供改进。不要羞于在 [crates.io](https://crates.io/)<!-- ignore --> 上共享对你有用的代码因为它很有可能对别人也很有用!
2 changes: 1 addition & 1 deletion src/ch15-01-box.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

### Box 允许创建递归类型

**递归类型**_recursive type_的值可以拥有另一个同类型的值作为其的一部分。这会产生一个问题因为 Rust 需要在编译时知道类型占用多少空间。递归类型的值嵌套理论上可以无限的进行下去,所以 Rust 不知道递归类型需要多少空间。因为 box 有一个已知的大小,所以通过在循环类型定义中插入 box,就可以创建递归类型了。
**递归类型**_recursive type_的值可以拥有另一个同类型的值作为其自身的一部分。但是这会产生一个问题,因为 Rust 需要在编译时知道类型占用多少空间。递归类型的值嵌套理论上可以无限地进行下去,所以 Rust 不知道递归类型需要多少空间。因为 box 有一个已知的大小,所以通过在循环类型定义中插入 box,就可以创建递归类型了。

作为一个递归类型的例子,让我们探索一下 _cons list_。这是一个函数式编程语言中常见的数据类型,来展示这个(递归类型)概念。除了递归之外,我们将要定义的 cons list 类型是很直白的,所以这个例子中的概念,在任何遇到更为复杂的涉及到递归类型的场景时都很实用。

Expand Down
2 changes: 1 addition & 1 deletion src/ch15-04-rc.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
大部分情况下所有权是非常明确的:可以准确地知道哪个变量拥有某个值。然而,有些情况单个值可能会有多个所有者。例如,在图数据结构中,多个边可能指向相同的节点,而这个节点从概念上讲为所有指向它的边所拥有。节点直到没有任何边指向它之前都不应该被清理因此也没有所有者。

为了启用多所有权需要显式地使用 Rust 类型 `Rc<T>`,其为 **引用计数**_reference counting_)的缩写。引用计数意味着记录一个值引用的数量来知晓这个值是否仍在被使用。如果某个值有零个引用,就代表没有任何有效引用并可以被清理。
为了启用多所有权需要显式地使用 Rust 类型 `Rc<T>`,其为 **引用计数**_reference counting_)的缩写。引用计数意味着记录一个值的引用数量来知晓这个值是否仍在被使用。如果某个值有零个引用,就代表没有任何有效引用并可以被清理。

可以将其想象为客厅中的电视。当一个人进来看电视时,他打开电视。其他人也可以进来看电视。当最后一个人离开房间时,他关掉电视因为它不再被使用了。如果某人在其他人还在看的时候就关掉了电视,正在看电视的人肯定会抓狂的!

Expand Down
2 changes: 1 addition & 1 deletion src/ch15-05-interior-mutability.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@

#### 内部可变性的用例:mock 对象

有时在测试中程序员会用某个类型替换另一个类型,以便观察特定的行为并断言它是被正确实现的。这个占位符类型被称为 **测试替身**_test double_)。可以把它想象成电影制作中的 “替身演员”("stunt double"),这是一个龙套进入并替代演员进行一些特定的高难度操作。测试替身在运行测试时替代某个类型。**mock 对象** 是特定类型的测试替身,它们记录测试过程中发生了什么以便可以断言操作是正确的。
有时在测试中程序员会用某个类型替换另一个类型,以便观察特定的行为并断言它是被正确实现的。这个占位符类型被称为 **测试替身**(_test double_)。就像电影制作中的替身演员(_stunt double_)一样,替代演员完成高难度的场景。测试替身在运行测试时替代某个类型。**mock 对象** 是特定类型的测试替身,它们记录测试过程中发生了什么以便可以断言操作是正确的。

虽然 Rust 中的对象与其他语言中的对象并不是一回事,Rust 也没有像其他语言那样在标准库中内建 mock 对象功能,不过我们确实可以创建一个与 mock 对象有着相同功能的结构体。

Expand Down
Loading

0 comments on commit a6a8134

Please sign in to comment.