mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-02-02 15:28:40 +08:00
Merge pull request #50 from YangFong/doc-all
docs: Optimize the description
This commit is contained in:
commit
5115044b9b
@ -4,7 +4,7 @@ Edsger W. Dijkstra 在其 1972 年的文章【谦卑的开发者】(“The Hum
|
||||
|
||||
程序的正确性意味着代码如我们期望的那样运行。Rust 是一个相当注重正确性的编程语言,不过正确性是一个难以证明的复杂主题。Rust 的类型系统在此问题上下了很大的功夫,不过它不可能捕获所有种类的错误。为此,Rust 也在语言本身包含了编写软件测试的支持。
|
||||
|
||||
例如,我们可以编写一个叫做 `add_two` 的将传递给它的值加二的函数。它的签名有一个整型参数并返回一个整型值。当实现和编译这个函数时,Rust 会进行所有目前我们已经见过的类型检查和借用检查,例如,这些检查会确保我们不会传递 `String` 或无效的引用给这个函数。Rust 所 **不能** 检查的是这个函数是否会准确的完成我们期望的工作:返回参数加二后的值,而不是比如说参数加 10 或减 50 的值!这也就是测试出场的地方。
|
||||
例如,我们可以编写一个叫做 `add_two` 的将传递给它的值加二的函数。它的签名有一个整型参数并返回一个整型值。当实现和编译这个函数时,Rust 会进行所有目前我们已经见过的类型检查和借用检查,例如,这些检查会确保我们不会传递 `String` 或无效的引用给这个函数。Rust 所 **不能** 检查的是这个函数是否会准确的完成我们期望的工作:返回参数加 2 后的值,而不是比如说参数加 10 或减 50 的值!这也就是测试出场的地方。
|
||||
|
||||
我们可以编写测试断言,比如说,当传递 `3` 给 `add_two` 函数时,返回值是 `5`。无论何时对代码进行修改,都可以运行测试来确保任何现存的正确行为没有被改变。
|
||||
|
||||
|
@ -69,7 +69,7 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
|
||||
Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这几行之后,可以看到 `running 1 test` 这一行。下一行显示了生成的测试函数的名称,它是 `it_works`,以及测试的运行结果,`ok`。接着可以看到全体测试运行结果的摘要:`test result: ok.` 意味着所有测试都通过了。`1 passed; 0 failed` 表示通过或失败的测试数量。
|
||||
|
||||
因为之前我们并没有将任何测试标记为忽略,所以摘要中会显示 `0 ignored`。我们也没有过滤需要运行的测试,所以摘要中会显示`0 filtered out`。在下一部分 [“控制测试如何运行”][controlling-how-tests-are-run] 会讨论忽略和过滤测试。
|
||||
因为之前我们并没有将任何测试标记为忽略,所以摘要中会显示 `0 ignored`。我们也没有过滤需要运行的测试,所以摘要中会显示 `0 filtered out`。在下一部分 [“控制测试如何运行”][controlling-how-tests-are-run] 会讨论忽略和过滤测试。
|
||||
|
||||
`0 measured` 统计是针对性能测试的。性能测试(benchmark tests)在编写本书时,仍只能用于 Rust 开发版(nightly Rust)。请查看 [性能测试的文档][bench] 了解更多。
|
||||
|
||||
|
@ -6,9 +6,9 @@ Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著
|
||||
|
||||
更具体的,我们将要涉及:
|
||||
|
||||
* **闭包**(*Closures*),一个可以储存在变量里的类似函数的结构
|
||||
* **迭代器**(*Iterators*),一种处理元素序列的方式
|
||||
* 如何使用这些功能来改进第 12 章的 I/O 项目。
|
||||
* 这两个功能的性能。(**剧透警告:** 他们的速度超乎你的想象!)
|
||||
- **闭包**(*Closures*),一个可以储存在变量里的类似函数的结构
|
||||
- **迭代器**(*Iterators*),一种处理元素序列的方式
|
||||
- 如何使用这些功能来改进第 12 章的 I/O 项目
|
||||
- 这两个功能的性能(**剧透警告:** 他们的速度超乎你的想象!)
|
||||
|
||||
还有其它受函数式风格影响的 Rust 功能,比如模式匹配和枚举,这些已经在其他章节中讲到过了。掌握闭包和迭代器则是编写符合语言风格的高性能 Rust 代码的重要一环,所以我们将专门用一整章来讲解他们。
|
||||
还有其它受函数式风格影响的 Rust 功能,比如模式匹配和枚举,这些已经在其他章节中讲到过了。掌握闭包和迭代器是编写符合语言风格、高性能 Rust 代码重要的一环,所以我们将专门用一整章来讲解它们。
|
||||
|
@ -29,8 +29,8 @@ fn simulated_expensive_calculation(intensity: u32) -> u32 {
|
||||
|
||||
所需的输入有这些:
|
||||
|
||||
* 一个来自用户的 intensity 数字,请求健身计划时指定,它代表用户喜好低强度还是高强度健身。
|
||||
* 一个随机数,其会在健身计划中生成变化。
|
||||
- 一个来自用户的 intensity 数字,请求健身计划时指定,它代表用户喜好低强度还是高强度健身。
|
||||
- 一个随机数,其会在健身计划中生成变化。
|
||||
|
||||
程序的输出将会是建议的锻炼计划。示例 13-2 展示了我们将要使用的 `main` 函数:
|
||||
|
||||
@ -92,7 +92,7 @@ fn generate_workout(intensity: u32, random_number: u32) {
|
||||
|
||||
<span class="caption">示例 13-3:程序的业务逻辑,它根据输入并调用 `simulated_expensive_calculation` 函数来打印出健身计划</span>
|
||||
|
||||
示例 13-3 中的代码有多处调用了慢计算函数 `simulated_expensive_calculation` 。第一个 `if` 块调用了 `simulated_expensive_calculation` 两次, `else` 中的 `if` 没有调用它,而第二个 `else` 中的代码调用了它一次。
|
||||
示例 13-3 中的代码有多处调用了慢计算函数 `simulated_expensive_calculation`。第一个 `if` 块调用了 `simulated_expensive_calculation` 两次, `else` 中的 `if` 没有调用它,而第二个 `else` 中的代码调用了它一次。
|
||||
|
||||
`generate_workout` 函数的期望行为是首先检查用户需要低强度(由小于 25 的系数表示)锻炼还是高强度(25 或以上)锻炼。
|
||||
|
||||
@ -525,9 +525,9 @@ error[E0434]: can't capture dynamic environment in a fn item; use the || { ...
|
||||
|
||||
闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。这三种捕获值的方式被编码为如下三个 `Fn` trait:
|
||||
|
||||
* `FnOnce` 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 **环境**,*environment*。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 `Once` 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次。
|
||||
* `FnMut` 获取可变的借用值所以可以改变其环境
|
||||
* `Fn` 从其环境获取不可变的借用值
|
||||
- `FnOnce` 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 **环境**,*environment*。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 `Once` 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次。
|
||||
- `FnMut` 获取可变的借用值所以可以改变其环境
|
||||
- `Fn` 从其环境获取不可变的借用值
|
||||
|
||||
当创建一个闭包时,Rust 根据其如何使用环境中变量来推断我们希望如何引用环境。由于所有闭包都可以被调用至少一次,所以所有闭包都实现了 `FnOnce` 。那些并没有移动被捕获变量的所有权到闭包内的闭包也实现了 `FnMut` ,而不需要对被捕获的变量进行可变访问的闭包则也实现了 `Fn` 。 在示例 13-12 中,`equal_to_x` 闭包不可变的借用了 `x`(所以 `equal_to_x` 具有 `Fn` trait),因为闭包体只需要读取 `x` 的值。
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
# 智能指针
|
||||
|
||||
**指针** (*pointer*)是一个包含内存地址的变量的通用概念。这个地址引用,或 “指向”(points at)一些其他数据。Rust 中最常见的指针是第 4 章介绍的 **引用**(*reference*)。引用以 `&` 符号为标志并借用了他们所指向的值。除了引用数据没有任何其他特殊功能。它们也没有任何额外开销,所以应用得最多。
|
||||
**指针** (*pointer*)是一个包含内存地址的变量的通用概念。这个地址引用,或 “指向”(points at)一些其他数据。Rust 中最常见的指针是第 4 章介绍的 **引用**(*reference*)。引用以 `&` 符号为标志并借用了它们所指向的值。除了引用数据没有任何其他特殊功能。它们也没有任何额外开销,所以应用得最多。
|
||||
|
||||
另一方面,**智能指针**(*smart pointers*)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和功能。智能指针的概念并不为 Rust 所独有;其起源于 C++ 并存在于其他语言中。Rust 标准库中不同的智能指针提供了多于引用的额外功能。本章将会探索的一个例子便是 **引用计数** (*reference counting*)智能指针类型,其允许数据有多个所有者。引用计数智能指针记录总共有多少个所有者,并当没有任何所有者时负责清理数据。
|
||||
另一方面,**智能指针**(*smart pointers*)是一类数据结构,它们的表现类似指针,但是也拥有额外的元数据和功能。智能指针的概念并非 Rust 独有:其起源于 C++,也存在于其他语言中。Rust 标准库中不同的智能指针提供了多于引用的额外功能。本章将会探索的一个例子便是 **引用计数** (*reference counting*)智能指针类型,其允许数据有多个所有者。引用计数智能指针记录总共有多少个所有者,并当没有任何所有者时负责清理数据。
|
||||
|
||||
在 Rust 中,普通引用和智能指针的一个额外的区别是引用是一类只借用数据的指针;相反,在大部分情况下,智能指针 **拥有** 他们指向的数据。
|
||||
在 Rust 中,普通引用和智能指针的一个额外的区别是引用是一类只借用数据的指针;相反,在大部分情况下,智能指针 **拥有** 它们指向的数据。
|
||||
|
||||
实际上本书中已经出现过一些智能指针,比如第 8 章的 `String` 和 `Vec<T>`,虽然当时我们并不这么称呼它们。这些类型都属于智能指针因为它们拥有一些数据并允许你修改它们。它们也带有元数据(比如他们的容量)和额外的功能或保证(`String` 的数据总是有效的 UTF-8 编码)。
|
||||
实际上本书中已经出现过一些智能指针,比如第 8 章的 `String` 和 `Vec<T>`,虽然当时我们并不这么称呼它们。这些类型都属于智能指针因为它们拥有一些数据并允许你修改它们。它们也带有元数据(比如它们的容量)和额外的功能或保证(`String` 的数据总是有效的 UTF-8 编码)。
|
||||
|
||||
智能指针通常使用结构体实现。智能指针区别于常规结构体的显著特性在于其实现了 `Deref` 和 `Drop` trait。`Deref` trait 允许智能指针结构体实例表现的像引用一样,这样就可以编写既用于引用、又用于智能指针的代码。`Drop` trait 允许我们自定义当智能指针离开作用域时运行的代码。本章会讨论这些 trait 以及为什么对于智能指针来说他们很重要。
|
||||
智能指针通常使用结构体实现。智能指针区别于常规结构体的显著特性在于其实现了 `Deref` 和 `Drop` trait。`Deref` trait 允许智能指针结构体实例表现的像引用一样,这样就可以编写既用于引用、又用于智能指针的代码。`Drop` trait 允许我们自定义当智能指针离开作用域时运行的代码。本章会讨论这些 trait 以及为什么对于智能指针来说它们很重要。
|
||||
|
||||
考虑到智能指针是一个在 Rust 经常被使用的通用设计模式,本章并不会覆盖所有现存的智能指针。很多库都有自己的智能指针而你也可以编写属于你自己的智能指针。这里将会讲到的是来自标准库中最常用的一些:
|
||||
|
||||
* `Box<T>`,用于在堆上分配值
|
||||
* `Rc<T>`,一个引用计数类型,其数据可以有多个所有者
|
||||
* `Ref<T>` 和 `RefMut<T>`,通过 `RefCell<T>` 访问。( `RefCell<T>` 是一个在运行时而不是在编译时执行借用规则的类型)。
|
||||
- `Box<T>`,用于在堆上分配值
|
||||
- `Rc<T>`,一个引用计数类型,其数据可以有多个所有者
|
||||
- `Ref<T>` 和 `RefMut<T>`,通过 `RefCell<T>` 访问( `RefCell<T>` 是一个在运行时而不是在编译时执行借用规则的类型)
|
||||
|
||||
另外我们会涉及 **内部可变性**(*interior mutability*)模式,这是不可变类型暴露出改变其内部值的 API。我们也会讨论 **引用循环**(*reference cycles*)会如何泄漏内存,以及如何避免。
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
## 使用`Box <T>`指向堆上的数据
|
||||
## 使用 `Box<T>` 指向堆上的数据
|
||||
|
||||
最简单直接的智能指针是 _box_,其类型是 `Box<T>`。 box 允许你将一个值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。如果你想回顾一下栈与堆的区别请参考第 4 章。
|
||||
|
||||
@ -112,7 +112,7 @@ enum Message {
|
||||
|
||||
当 Rust 需要知道要为 `Message` 值分配多少空间时,它可以检查每一个成员并发现 `Message::Quit` 并不需要任何空间,`Message::Move` 需要足够储存两个 `i32` 值的空间,依此类推。因此,`Message` 值所需的空间等于储存其最大成员的空间大小。
|
||||
|
||||
与此相对当 Rust 编译器检查像示例 15-2 中的 `List` 这样的递归类型时会发生什么呢。编译器尝试计算出储存一个 `List` 枚举需要多少内存,并开始检查 `Cons` 成员,那么 `Cons` 需要的空间等于 `i32` 的大小加上 `List` 的大小。为了计算 `List` 需要多少内存,它检查其成员,从 `Cons` 成员开始。`Cons`成员储存了一个 `i32` 值和一个`List`值,这样的计算将无限进行下去,如图 15-1 所示:
|
||||
与此相对当 Rust 编译器检查像示例 15-2 中的 `List` 这样的递归类型时会发生什么呢。编译器尝试计算出储存一个 `List` 枚举需要多少内存,并开始检查 `Cons` 成员,那么 `Cons` 需要的空间等于 `i32` 的大小加上 `List` 的大小。为了计算 `List` 需要多少内存,它检查其成员,从 `Cons` 成员开始。`Cons` 成员储存了一个 `i32` 值和一个 `List` 值,这样的计算将无限进行下去,如图 15-1 所示:
|
||||
|
||||
<img alt="An infinite Cons list" src="img/trpl15-01.svg" class="center" style="width: 50%;" />
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user