diff --git a/src/SUMMARY.md b/src/SUMMARY.md index cf6c5e7..fbf4553 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -102,4 +102,19 @@ - [模式用来匹配值的结构](ch18-00-patterns.md) - [所有可能会用到模式的位置](ch18-01-all-the-places-for-patterns.md) - [refutable:何时模式可能会匹配失败](ch18-02-refutability.md) - - [模式的全部语法](ch18-03-pattern-syntax.md) \ No newline at end of file + - [模式的全部语法](ch18-03-pattern-syntax.md) + +- [高级特征](ch19-00-advanced-features.md) + - [不安全的 Rust](ch19-01-unsafe-rust.md) + - [高级生命周期](ch19-02-advanced-lifetimes.md) + - [高级 trait](ch19-03-advanced-traits.md) + - [高级类型](ch19-04-advanced-types.md) + - [高级函数与闭包](ch19-05-advanced-functions-and-closures.md) + +- [Final Project: Building a Multithreaded Web Server](ch20-00-final-project-a-web-server.md) + - [A Single Threaded Web Server](ch20-01-single-threaded.md) + - [How Slow Requests Affect Throughput](ch20-02-slow-requests.md) + - [Designing the Thread Pool Interface](ch20-03-designing-the-interface.md) + - [Creating the Thread Pool and Storing Threads](ch20-04-storing-threads.md) + - [Sending Requests to Threads Via Channels](ch20-05-sending-requests-via-channels.md) + - [Graceful Shutdown and Cleanup](ch20-06-graceful-shutdown-and-cleanup.md) \ No newline at end of file diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index 3c81e09..7b04c20 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -56,7 +56,7 @@ fn calculate_length(s: &String) -> usize { // s is a reference to a String 变量`s`有效的作用域与函数参数的作用域一样,不过当引用离开作用域后并不丢弃它指向的数据因为我们没有所有权。函数使用引用而不是实际值作为参数意味着无需返回值来交还所有权,因为就不曾拥有它。 -我们将获取引用作为函数参数称为**借用**(*borrowing*)。正如现实生活中,如果一个人拥有某样东西,你可以从它哪里借来。当你使用完毕,必须还回去。 +我们将获取引用作为函数参数称为**借用**(*borrowing*)。正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。 如果我们尝试修改借用的变量呢?尝试列表 4-9 中的代码。剧透:这行不通! diff --git a/src/ch07-01-mod-and-the-filesystem.md b/src/ch07-01-mod-and-the-filesystem.md index d64a786..1f6c415 100644 --- a/src/ch07-01-mod-and-the-filesystem.md +++ b/src/ch07-01-mod-and-the-filesystem.md @@ -105,7 +105,7 @@ communicator └── client ``` -可以看到列表 7-2 中,`client`是`network`的子模块,而不是它的同级模块。更为负责的项目可以有很多的模块,所以他们需要符合逻辑地组合在一起以便记录他们。在项目中“符合逻辑”的意义全凭你得理解和库的用户对你项目领域的认识。利用我们这里讲到的技术来创建同级模块和嵌套的模块将是你会喜欢的结构。 +可以看到列表 7-2 中,`client`是`network`的子模块,而不是它的同级模块。更为负责的项目可以有很多的模块,所以他们需要符合逻辑地组合在一起以便记录他们。在项目中“符合逻辑”的意义全凭你的理解和库的用户对你项目领域的认识。利用我们这里讲到的技术来创建同级模块和嵌套的模块将是你会喜欢的结构。 ### 将模块移动到其他文件 @@ -348,4 +348,4 @@ communicator 模块自身则应该使用`mod`关键字定义于父模块的文件中。 -接下来,我们讨论一下`pub`关键字,并除掉那些警告! \ No newline at end of file +接下来,我们讨论一下`pub`关键字,并除掉那些警告! diff --git a/src/ch09-02-recoverable-errors-with-result.md b/src/ch09-02-recoverable-errors-with-result.md index 7d43eff..7c6544e 100644 --- a/src/ch09-02-recoverable-errors-with-result.md +++ b/src/ch09-02-recoverable-errors-with-result.md @@ -4,7 +4,7 @@ >
> commit e6d6caab41471f7115a621029bd428a812c5260e -大部分错误并没有严重到需要程序完全停止执行。有时,一个函数会因为一个容易理解并做出反映的原因失败。例如,如果尝试打开一个文件不过由于文件并不存在而操作就失败,这是我们可能想要创建这个文件而不是终止进程。 +大部分错误并没有严重到需要程序完全停止执行。有时,一个函数会因为一个容易理解并做出反映的原因失败。例如,如果尝试打开一个文件不过由于文件并不存在而操作就失败,这时我们可能想要创建这个文件而不是终止进程。 回忆一下第二章“使用`Result`类型来处理潜在的错误”部分中的那个`Result`枚举,它定义有如下两个成员,`Ok`和`Err`: @@ -298,4 +298,4 @@ error[E0308]: mismatched types 错误指出存在不匹配的类型:`main`函数返回一个`()`类型,而`?`返回一个`Result`。编写不返回`Result`的函数时,如果调用其他返回`Result`的函数,需要使用`match`或者`Result`的方法之一来处理它,而不能用`?`将潜在的错误传播给调用者。 -现在我们讨论过了调用`panic!`或返回`Result`的细节,是时候返回他们各自适合哪些场景的话题了。 \ No newline at end of file +现在我们讨论过了调用`panic!`或返回`Result`的细节,是时候返回他们各自适合哪些场景的话题了。 diff --git a/src/ch12-04-testing-the-librarys-functionality.md b/src/ch12-04-testing-the-librarys-functionality.md index cded976..bdcf7a5 100644 --- a/src/ch12-04-testing-the-librarys-functionality.md +++ b/src/ch12-04-testing-the-librarys-functionality.md @@ -13,7 +13,7 @@ 3. 重构刚刚增加或修改的代码,并确保测试仍然能通过。 4. 重复上述步骤! -这只是众多编写软件的方法之一,不过 TDD 有助于驱动代码的设计。在编写能使测试通过的代码之前编写测测试有助于在开发过程中保持高测试覆盖率。 +这只是众多编写软件的方法之一,不过 TDD 有助于驱动代码的设计。在编写能使测试通过的代码之前编写测试有助于在开发过程中保持高测试覆盖率。 我们将测试驱动实现`greprs`实际在文件内容中搜索查询字符串并返回匹配的行列表的部分。我们将在一个叫做`search`的函数中增加这些功能。 @@ -296,4 +296,4 @@ $ cargo run monomorphization poem.txt 非常好!我们创建了一个属于自己的经典工具,并学习了很多如何组织程序的知识。我们还学习了一些文件输入输出、生命周期、测试和命令行解析的内容。 -现在如果你希望的话请随意移动到第十三章。为了使这个项目章节更丰满,我们将简要的展示如何处理环境变量和打印到标准错误,这两者在编写命令行程序时都很有用。 \ No newline at end of file +现在如果你希望的话请随意移动到第十三章。为了使这个项目章节更丰满,我们将简要的展示如何处理环境变量和打印到标准错误,这两者在编写命令行程序时都很有用。 diff --git a/src/ch17-02-trait-objects.md b/src/ch17-02-trait-objects.md index f110314..fc09d6a 100644 --- a/src/ch17-02-trait-objects.md +++ b/src/ch17-02-trait-objects.md @@ -4,25 +4,26 @@ >
> commit 67876e3ef5323ce9d394f3ea6b08cb3d173d9ba9 - 在第八章,我们谈到了 vector 只能存储同种类型元素的局限。在列表 8-1 中有一个例子,其中定义了存放包含整型、浮点型和文本型成员的枚举类型`SpreadsheetCell`,这样就可以在每一个单元格储存不同类型的数据,并使得 vector 仍然代表一行单元格。当编译时就知道类型集合全部元素的情况下,这种方案是可行的。 + 在第八章中,我们谈到了 vector 只能存储同种类型元素的局限。在列表 8-1 中有一个例子,其中定义了一个拥有分别存放整型、浮点型和文本型成员的枚举类型 `SpreadsheetCell`,使用这个枚举的 vector 可以在每一个单元格(cell)中储存不同类型的数据,并使得 vector 整体仍然代表一行(row)单元格。这当编译代码时就知道希望可以交替使用的类型为固定集合的情况下是可行的。 -有时,我们希望使用的类型的集合对于使用库的程序员来说是可扩展的。例如,很多图形用户接口(GUI)工具有一个条目列表的概念,它通过遍历列表并对每一个条目调用 `draw` 方法来绘制在屏幕上。我们将要创建一个叫做 `rust_gui` 的包含一个 GUI 库结构的库 crate。GUI 库可以包含一些供开发者使用的类型,比如 `Button` 或 `TextField`。使用 `rust_gui` 的程序员会想要创建更多可以绘制在屏幕上的类型:一个程序员可能会增加一个 `Image`,而另一个可能会增加一个 `SelectBox`。我们不会在本章节实现一个功能完善的 GUI 库,不过会展示各个部分是如何结合在一起的。 +有时,我们希望使用的类型的集合对于使用库的程序员来说是可扩展的。例如,很多图形用户接口(GUI)工具有一个项目列表的概念,它通过遍历列表并调用每一个项目的 `draw` 方法来将其绘制到屏幕上。我们将要创建一个叫做 `rust_gui` 的库 crate,它含一个 GUI 库的结构。这个 GUI 库包含一些可供开发者使用的类型,比如 `Button` 或 `TextField`。使用 `rust_gui` 的程序员会想要创建更多可以绘制在屏幕上的类型:其中一些可能会增加一个 `Image`,而另一些可能会增加一个 `SelectBox`。本章节并不准备实现一个功能完善的 GUI 库,不过会展示其中各个部分是如何结合在一起的。 -当写 `rust_gui` 库时,我们不知道其他程序员需要什么类型,所以无法定义一个 `enum` 来包含所有的类型。然而 `rust_gui` 需要跟踪所有这些不同类型的值,需要有在每个值上调用 `draw` 方法能力。我们的 GUI 库不需要确切地知道调用 `draw` 方法会发生什么,只需要有可用的方法供我们调用。 +编写 `rust_gui` 库时,我们并不知道其他程序员想要创建的全部类型,所以无法定义一个 `enum` 来包含所有这些类型。我们所要做的是使 `rust_gui` 能够记录一系列不同类型的值,并能够对其中每一个值调用 `draw` 方法。 GUI 库不需要知道当调用 `draw` 方法时具体会发生什么,只需提供这些值可供调用的方法即可。 -在可以继承的语言里,我们会定义一个名为 `Component` 的类,该类上有一个`draw`方法。其他的类比如`Button`、`Image`和`SelectBox`会从`Component`继承并拥有`draw`方法。它们各自覆写`draw`方法以自定义行为,但是框架会把所有的类型当作是`Component`的实例,并在其上调用`draw`。 +在拥有继承的语言中,我们可能定义一个名为 `Component` 的类,该类上有一个 `draw` 方法。其他的类比如 `Button`、`Image` 和 `SelectBox` 会从 `Component` 派生并因此继承 `draw` 方法。它们各自都可以覆盖 `draw` 方法来定义自己的行为,但是框架会把所有这些类型当作是 `Component` 的实例,并在其上调用 `draw`。 -### 定义一个带有自定义行为的Trait +### 定义通用行为的 trait -不过,在Rust语言中,我们可以定义一个 `Draw` trait,包含名为 `draw` 的方法。我们定义一个由*trait对象*组成的vector,绑定了某种指针的trait,比如`&`引用或者一个`Box`智能指针。 +不过,在 Rust 中,我们可以定义一个 `Draw` trait,包含名为 `draw` 的方法。接着可以定义一个存放**trait 对象**(*trait +object*)的 vector,trait 对象是一个位于某些指针,比如 `&` 引用或 `Box` 智能指针,之后的 trait。第十九章会讲到为何 trait 对象必须位于指针之后的原因。 -之前提到,我们不会称结构体和枚举为对象,以区分其他语言的结构体和枚举对象。结构体或者枚举成员中的数据和`impl`块中的行为是分开的,而其他语言则是数据和行为被组合到一个对象里。Trait 对象更像其他语言的对象,因为他们将其指针指向的具体对象作为数据,将在 trait 中定义的方法作为行为,组合在了一起。但是,trait 对象和其他语言是不同的,我们不能向一个 trait 对象增加数据。trait 对象不像其他语言那样有用:它们的目的是允许从公有行为上抽象。 +之前提到过,我们并不将结构体与枚举称之为“对象”,以便与其他语言中的对象相区别。结构体与枚举和 `impl` 块中的行为是分开的,不同于其他语言中将数据和行为组合进一个称为对象的概念中。trait 对象将由指向具体对象的指针构成的数据和定义于 trait 中方法的行为结合在一起,从这种意义上说它**则**更类似其他语言中的对象。不过 trait 对象与其他语言中的对象是不同的,因为不能向 trait 对象增加数据。trait 对象并不像其他语言中的对象那么通用:他们(trait 对象)的作用是允许对通用行为的抽象。 -trait 对象定义了给定情况下应有的行为。当需要具有某种特性的不确定具体类型时,我们可以把 trait 对象当作 trait 使用。Rust 的类型系统会保证我们为 trait 对象带入的任何值会实现 trait 的方法。我们不需要在编译阶段知道所有可能的类型,却可以把所有的实例统一对待。列表 17-03 展示了如何定义一个名为`Draw`的带有`draw`方法的 trait。 +trait 对象定义了在给定情况下所需的行为。接着就可以在要使用具体类型或泛型的地方使用 trait 来作为 trait 对象。Rust 的类型系统会确保任何我们替换为 trait 对象的值都会实现了 trait 的方法。这样就无需在编译时就知道所有可能的类型,就能够用同样的方法处理所有的实例。列表 17-3 展示了如何定义一个带有 `draw` 方法的 trait `Draw`: 文件名: src/lib.rs @@ -36,7 +37,9 @@ pub trait Draw { -因为我们已经在第十章讨论过如何定义 trait,你可能比较熟悉。下面是新的定义:列表 17-4 有一个名为 `Screen` 的结构体,里面有一个名为 `components` 的 vector,`components` 的类型是 `Box`。`Box` 是一个 trait 对象:它是 `Box` 内部任意一个实现了 `Draw` trait 的类型的替身。 + + +因为第十章已经讨论过如何定义 trait,这看起来应该比较眼熟。接下来就是新内容了:列表 17-4 有一个名为 `Screen` 的结构体定义,它存放了一个叫做 `components` 的 `Box` 类型的 vector 。`Box` 是一个 trait 对象:它是 `Box` 中任何实现了 `Draw` trait 的类型的替身。 文件名: src/lib.rs @@ -52,7 +55,7 @@ pub struct Screen { 列表 17-4: 一个 `Screen` 结构体的定义,它带有一个字段`components`,其包含实现了 `Draw` trait 的 trait 对象的 vector -在 `Screen` 结构体上,我们将要定义一个 `run` 方法,该方法会在它的 `components` 上的每一个元素调用 `draw` 方法,如列表 17-5 所示: +在 `Screen` 结构体上,我们将定义一个 `run` 方法,该方法会对其 `components` 上的每一个元素调用 `draw` 方法,如列表 17-5 所示: 文件名: src/lib.rs @@ -74,10 +77,9 @@ impl Screen { } ``` -列表 17-5:在 `Screen` 上实现一个 `run` 方法,该方法在每个 component 上调用 `draw` 方法 - +列表 17-5:在 `Screen` 上实现一个 `run` 方法,该方法在每个 component 上调用 `draw` 方法 -这与带 trait 约束的泛型结构体不同(trait 约束泛型参数)。泛型参数一次只能被一个具体类型替代,而 trait 对象可以在运行时允许多种具体类型填充 trait 对象。比如,我们已经定义了 `Screen` 结构体使用泛型和一个 trait 约束,如列表 17-6 所示: +这与定义使用了带有 trait bound 的泛型类型参数的结构体不同。泛型类型参数一次只能替代一个具体的类型,而 trait 对象则允许在运行时替代多种具体类型。例如,可以像列表 17-6 那样定义使用泛型和 trait bound 的结构体 `Screen`: 文件名: src/lib.rs @@ -100,16 +102,15 @@ impl Screen } ``` -列表 17-6: 一种 `Screen` 结构体的替代实现,它的 `run` 方法使用通用类型和 trait 绑定 - +列表 17-6: 一种 `Screen` 结构体的替代实现,它的 `run` 方法使用泛型和 trait bound -这个例子中,`Screen` 实例所有组件类型必需全是 `Button`,或者全是 `TextField`。如果你的组件集合是单一类型的,那么可以优先使用泛型和 trait 约束,因为其使用的具体类型在编译阶段即可确定。 +这只允许我们拥有一个包含全是 `Button` 类型或者全是 `TextField` 类型的 component 列表的 `Screen` 实例。如果只拥有相同类型的集合,那么使用泛型和 trait bound 是更好的,因为在编译时使用具体类型其定义是单态(monomorphized)的。 -而 `Screen` 结构体内部的 `Vec>` trait 对象列表,则可以同时包含 `Box