mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
commit
5123368af8
@ -87,7 +87,7 @@ edited as such, can you check? -->
|
|||||||
I've hopefully cleared this up without needing to introduce repetition.
|
I've hopefully cleared this up without needing to introduce repetition.
|
||||||
/Carol-->
|
/Carol-->
|
||||||
|
|
||||||
你可能注意到了 vector 的第一个值是"target/debug/greprs",它是二进制我呢见的名称。其原因超出了本章介绍的范围,不过需要记住的是我们保存了所需的两个参数。
|
你可能注意到了 vector 的第一个值是"target/debug/greprs",它是我们二进制文件的名称。其原因超出了本章介绍的范围,不过需要记住的是我们保存了所需的两个参数。
|
||||||
|
|
||||||
### 将参数值保存进变量
|
### 将参数值保存进变量
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ currently bound together? And why does it imply that -->
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
> 注意:一些同学将这种当使用符合类型更为合适的时候使用基本类型的反模式称为**基本类型偏执**(*primitive obsession*)。
|
> 注意:一些同学将这种拒绝使用相对而言更为合适的复合类型而使用基本类型的模式称为**基本类型偏执**(*primitive obsession*)。
|
||||||
|
|
||||||
<!-- Ah, I see, so the problems here stem from using simple types to do tasks
|
<!-- Ah, I see, so the problems here stem from using simple types to do tasks
|
||||||
inefficiently, when a more complex task could handle it in ways that improve...
|
inefficiently, when a more complex task could handle it in ways that improve...
|
||||||
@ -219,7 +219,7 @@ but the index is 1', /stable-dist-rustc/build/src/libcollections/vec.rs:1307
|
|||||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||||
```
|
```
|
||||||
|
|
||||||
`index out of bounds: the len is 1 but the index is 1`是一个针对程序员的错误信息,这并不能真正帮助终端用户理解发生了什么和相反他们应该做什么。现在就让我们修复它吧。
|
`index out of bounds: the len is 1 but the index is 1`是一个针对程序员的错误信息,然而这并不能真正帮助终端用户理解发生了什么和他们应该做什么。现在就让我们修复它吧。
|
||||||
|
|
||||||
### 改善错误信息
|
### 改善错误信息
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ error: test failed
|
|||||||
|
|
||||||
#### 使用`lines`方法遍历每一行
|
#### 使用`lines`方法遍历每一行
|
||||||
|
|
||||||
Rust 有一个有助于一行一行遍历字符串的方法,出于方便它被成为`lines`,它如列表 12-17 这样工作:
|
Rust 有一个有助于一行一行遍历字符串的方法,出于方便它被命名为`lines`,它如列表 12-17 这样工作:
|
||||||
|
|
||||||
<span class="filename">Filename: src/lib.rs</span>
|
<span class="filename">Filename: src/lib.rs</span>
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
> <br>
|
> <br>
|
||||||
> commit 0db6a0a34886bf02feabcab8b430b5d332a8bdf5
|
> commit 0db6a0a34886bf02feabcab8b430b5d332a8bdf5
|
||||||
|
|
||||||
我们将用一个额外的功能来改进我们的工具:一个通过环境变量启用的大小写不敏感搜索的选项。我们将其设计为一个命令行参数并要求用户每次需要时都加上它,不过相反我们将使用环境变量。这允许用户设置环境变脸一次之后在整个终端会话中所有的搜索都将是大小写不敏感的了。
|
我们将用一个额外的功能来改进我们的工具:一个通过环境变量启用的大小写不敏感搜索的选项。我们将其设计为一个命令行参数并要求用户每次需要时都加上它,不过相反我们将使用环境变量。这允许用户设置环境变量一次之后在整个终端会话中所有的搜索都将是大小写不敏感的了。
|
||||||
|
|
||||||
### 编写一个大小写不敏感`search`函数的失败测试
|
### 编写一个大小写不敏感`search`函数的失败测试
|
||||||
|
|
||||||
|
@ -107,6 +107,6 @@ How dreary to be somebody!
|
|||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
在这一章中,我们回顾了目前为止的一些主要章节并涉及了如何在 Rust 中进行常规的 I/O 操作。通过使用命令行参数、文件、环境变量和`writeln!`宏与`writeln!`,现在你已经准备好编写命令行程序了。结合前几章的知识,你的代码将会是组织良好的,并能有效的将数据存储到合适的数据结构中、更好的处理错误,并且还是经过良好测试的。
|
在这一章中,我们回顾了目前为止的一些主要章节并涉及了如何在 Rust 中进行常规的 I/O 操作。通过使用命令行参数、文件、环境变量和`writeln!`宏与`stderr`,现在你已经准备好编写命令行程序了。结合前几章的知识,你的代码将会是组织良好的,并能有效的将数据存储到合适的数据结构中、更好的处理错误,并且还是经过良好测试的。
|
||||||
|
|
||||||
接下来,让我们探索如何利用一些 Rust 中受函数式编程语言影响的功能:闭包和迭代器。
|
接下来,让我们探索如何利用一些 Rust 中受函数式编程语言影响的功能:闭包和迭代器。
|
@ -72,7 +72,7 @@ let add_one_v3 = |x| { x + 1 }; // a closure eliding types
|
|||||||
let add_one_v4 = |x| x + 1 ; // without braces
|
let add_one_v4 = |x| x + 1 ; // without braces
|
||||||
```
|
```
|
||||||
|
|
||||||
定义闭包时并要求类型注解而在定义函数是要求的原因在于函数是显式暴露给用户的接口的一部分,所以为了严格的定义接口确保所有人都同意函数使用和返回的值类型是很重要的。但是闭包并不像函数那样用于暴露接口:他们存在于绑定中并直接被调用。强制标注类型就等于为了很小的优点而显著的降低了工程性(本末倒置)。
|
定义闭包时不要求类型注解而在定义函数时要求的原因在于函数是显式暴露给用户的接口的一部分,所以为了严格的定义接口确保所有人都同意函数使用和返回的值类型是很重要的。但是闭包并不像函数那样用于暴露接口:他们存在于绑定中并直接被调用。强制标注类型就等于为了很小的优点而显著的降低了工程性(本末倒置)。
|
||||||
|
|
||||||
不过闭包的定义确实会推断每一个参数和返回值的类型。例如,如果用`i8`调用列表 13-1 中没有类型注解的闭包,如果接着用`i32`调用同一闭包则会得到一个错误:
|
不过闭包的定义确实会推断每一个参数和返回值的类型。例如,如果用`i8`调用列表 13-1 中没有类型注解的闭包,如果接着用`i32`调用同一闭包则会得到一个错误:
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ vector 的`iter`方法允许从 vector 创建一个**迭代器**(*iterator*)
|
|||||||
|
|
||||||
1. 从 vector 中创建了一个迭代器。
|
1. 从 vector 中创建了一个迭代器。
|
||||||
2. 使用`map`适配器和一个闭包参数对每一个元素加一。
|
2. 使用`map`适配器和一个闭包参数对每一个元素加一。
|
||||||
3. 使用`collect`适配器来消费迭代去并生成了一个新的 vector。
|
3. 使用`collect`适配器来消费迭代器并生成了一个新的 vector。
|
||||||
|
|
||||||
这就是如何产生结果`[2, 3, 4]`的。如你所见,闭包是使用迭代器的很重要的一部分:他们提供了一个自定义类似`map`这样的迭代器适配器的行为的方法。
|
这就是如何产生结果`[2, 3, 4]`的。如你所见,闭包是使用迭代器的很重要的一部分:他们提供了一个自定义类似`map`这样的迭代器适配器的行为的方法。
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ nothing unless consumed, #[warn(unused_must_use)] on by default
|
|||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
```
|
```
|
||||||
|
|
||||||
这个警告是因为迭代器适配器实际上并不自己进行处理。他们需要一些其他方法来触发迭代器链的计算。我们称之为**消费迭代器**(*consuming adaptors*),而`collect`就是其中之一。
|
这个警告是因为迭代器适配器实际上并不自己进行处理。他们需要一些其他方法来触发迭代器链的计算。我们称之为**消费适配器**(*consuming adaptors*),而`collect`就是其中之一。
|
||||||
|
|
||||||
那么如何知道迭代器方法是否消费了迭代器呢?还有哪些适配器是可用的呢?为此,让我们看看`Iterator` trait。
|
那么如何知道迭代器方法是否消费了迭代器呢?还有哪些适配器是可用的呢?为此,让我们看看`Iterator` trait。
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ trait Iterator {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这里有一些还未讲到的新语法:`type Item`和`Self::Item`定义了这个 trait 的**关联类型**(*associated type*),第XX章会讲到关联类型。现在所有你需要知道就是这些代码表示`Iterator` trait 要求你也定义一个`Item`类型,而这个`Item`类型用作`next`方法的返回值。换句话说,`Item`类型将是迭代器返回的元素的类型。
|
这里有一些还未讲到的新语法:`type Item`和`Self::Item`定义了这个 trait 的**关联类型**(*associated type*),第19章会讲到关联类型。现在所有你需要知道就是这些代码表示`Iterator` trait 要求你也定义一个`Item`类型,而这个`Item`类型用作`next`方法的返回值。换句话说,`Item`类型将是迭代器返回的元素的类型。
|
||||||
|
|
||||||
让我们使用`Iterator` trait 来创建一个从一数到五的迭代器`Counter`。首先,需要创建一个结构体来存放迭代器的当前状态,它有一个`u32`的字段`count`。我们也定义了一个`new`方法,当然这并不是必须的。因为我们希望`Counter`能从一数到五,所以它总是从零开始:
|
让我们使用`Iterator` trait 来创建一个从一数到五的迭代器`Counter`。首先,需要创建一个结构体来存放迭代器的当前状态,它有一个`u32`的字段`count`。我们也定义了一个`new`方法,当然这并不是必须的。因为我们希望`Counter`能从一数到五,所以它总是从零开始:
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ test bench_grep_for ... bench: 19,620,300 ns/iter (+/- 915,700)
|
|||||||
test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
|
test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
|
||||||
```
|
```
|
||||||
|
|
||||||
结果迭代器版本还要稍微快一点!这里我们将不会查看性能测试的代码,光是这一点并不是为了证明他们是完全等同的,而是提供了一个大体上比较这两种实现的方向。对于**真正**的性能测试,将会检查不同长度的文本、不同的搜索单词、不同长度的单词和所有其他的可变情况。这里所要表达的是:迭代器,作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能代码。迭代器是 Rust 的**零成本抽象**(*zero-cost abstractions*)之一,它意味着抽象并不会强加运行时开销,它与本贾尼·斯特劳斯特卢普,C++ 的设计和实现者所定义的**零开销**(*zero-overhead*)如出一辙:
|
结果迭代器版本还要稍微快一点!这里我们将不会查看性能测试的代码,我们的目的并不是为了证明他们是完全等同的,而是得出一个怎样比较这两种实现方式的基本思路。对于**真正**的性能测试,将会检查不同长度的文本、不同的搜索单词、不同长度的单词和所有其他的可变情况。这里所要表达的是:迭代器,作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能代码。迭代器是 Rust 的**零成本抽象**(*zero-cost abstractions*)之一,它意味着抽象并不会强加运行时开销,它与本贾尼·斯特劳斯特卢普,C++ 的设计和实现者所定义的**零开销**(*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.
|
> 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.
|
||||||
>
|
>
|
||||||
@ -21,7 +21,7 @@ test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
|
|||||||
>
|
>
|
||||||
> - 本贾尼·斯特劳斯特卢普 "Foundations of C++"
|
> - 本贾尼·斯特劳斯特卢普 "Foundations of C++"
|
||||||
|
|
||||||
作为另一个例子,这里有一些来自于音频解码器的代码。这些代码使用迭代器链来对作用域中的三个变量进行了某种数学计算:一个叫`buffer`的数据 slice、一个 12 个系数列表的`coefficients`、和一个移位位数的`qlp_shift`。例子中声明了这些变量但并没有提供任何值;虽然这些代码在其上下文之外没有什么意义,不过仍是一个简洁的现实中的例子,来展示 Rust 如何将高级概念转换为底层代码:
|
作为另一个例子,这里有一些来自于音频解码器的代码。这些代码使用迭代器链来对作用域中的三个变量进行了某种数学计算:一个叫`buffer`的数据 slice、一个有12个元素的数组`coefficients`、和一个代表移位位数的`qlp_shift`。例子中声明了这些变量但并没有提供任何值;虽然这些代码在其上下文之外没有什么意义,不过仍是一个简洁的现实中的例子,来展示 Rust 如何将高级概念转换为底层代码:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
let buffer: &mut [i32];
|
let buffer: &mut [i32];
|
||||||
|
@ -122,7 +122,7 @@ fn main() {
|
|||||||
<span class="caption">Listing 14-3: A program using the `art` crate's items
|
<span class="caption">Listing 14-3: A program using the `art` crate's items
|
||||||
with its internal structure exported</span>
|
with its internal structure exported</span>
|
||||||
|
|
||||||
使用并不需要知道`PrimaryColor`和`SecondaryColor`位于`kinds`模块中和`mix`位于`utils`模块中;这些结构对于内部组织是有帮助的,不过对于外部的观点来说没有什么意义。
|
库的用户并不需要知道`PrimaryColor`和`SecondaryColor`位于`kinds`模块中和`mix`位于`utils`模块中;这些结构对于内部组织是有帮助的,不过对于外部的观点来说没有什么意义。
|
||||||
|
|
||||||
为此,可以选择在列表 14-2 中增加如下`pub use`语句来将这些类型重新导出到顶级结构,如列表 14-4 所示:
|
为此,可以选择在列表 14-2 中增加如下`pub use`语句来将这些类型重新导出到顶级结构,如列表 14-4 所示:
|
||||||
|
|
||||||
@ -200,7 +200,7 @@ upload metadata
|
|||||||
|
|
||||||
我们可以在包的 *Cargo.toml* 文件中包含更多的信息。其中一些字段是可选的,不过描述和 license 是发布所必须的,因为这样人们才能知道 crate 是干什么的已经在什么样的条款下可以使用他们。
|
我们可以在包的 *Cargo.toml* 文件中包含更多的信息。其中一些字段是可选的,不过描述和 license 是发布所必须的,因为这样人们才能知道 crate 是干什么的已经在什么样的条款下可以使用他们。
|
||||||
|
|
||||||
描述连同 crate 一起出现在搜索结果和 crate 页面中。描述通常是一两句话。`license`字段获取一个 license 标识符值,其可能的值由 Linux 基金会的[Software Package Data Exchange (SPDX)][spdx]指定。如果你想要使用一个并存在于此的 license,则不使用`license`值,使用`license-file`来指定项目中包含你想要使用的 license 的文本的文件名。
|
描述连同 crate 一起出现在搜索结果和 crate 页面中。描述通常是一两句话。`license`字段获取一个 license 标识符值,其可能的值由 Linux 基金会的[Software Package Data Exchange (SPDX)][spdx]指定。如果你想要使用一个不存在于SPDX的 license,则不使用`license`值,使用`license-file`来指定项目中包含你想要使用的 license 的文本的文件名。
|
||||||
|
|
||||||
关于项目所适用的 license 的指导超出了本书的范畴。很多 Rust 社区成员选择与 Rust 自身相同的 license,它是一个双许可的`MIT/Apache-2.0`,这表明可以通过斜杠来分隔指定多个 license。所以一个准备好发布的项目的 *Cargo.toml* 文件看起来像这样:
|
关于项目所适用的 license 的指导超出了本书的范畴。很多 Rust 社区成员选择与 Rust 自身相同的 license,它是一个双许可的`MIT/Apache-2.0`,这表明可以通过斜杠来分隔指定多个 license。所以一个准备好发布的项目的 *Cargo.toml* 文件看起来像这样:
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ running 0 tests
|
|||||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
|
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
|
||||||
```
|
```
|
||||||
|
|
||||||
等等,零个测试?我们不是刚增加了一个吗?如果我们观察输出,就不难发现在工作空间中的`cargo test`只运行顶层 crate 的测试。为了运行其他 crate 的测试,需要使用`-p`参数来表明我们希望与逆行测试包的测试:
|
等等,零个测试?我们不是刚增加了一个吗?如果我们观察输出,就不难发现在工作空间中的`cargo test`只运行顶层 crate 的测试。为了运行其他 crate 的测试,需要使用`-p`参数来表明我们希望运行指定包的测试:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cargo test -p add-one
|
$ cargo test -p add-one
|
||||||
|
Loading…
Reference in New Issue
Block a user