mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
Fix texts by use autocorrect --fix
.
This commit is contained in:
parent
23673cb6e7
commit
483ea9f308
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
// ANCHOR: here
|
||||
{ // s 在这里无效, 它尚未声明
|
||||
{ // s 在这里无效,它尚未声明
|
||||
let s = "hello"; // 从此处起,s 是有效的
|
||||
|
||||
// 使用 s
|
||||
|
@ -10,7 +10,7 @@ fn main() {
|
||||
// 但 i32 是 Copy 的,
|
||||
// 所以在后面可继续使用 x
|
||||
|
||||
} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
|
||||
} // 这里,x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
|
||||
// 没有特殊之处
|
||||
|
||||
fn takes_ownership(some_string: String) { // some_string 进入作用域
|
||||
|
@ -5,16 +5,16 @@ fn main() {
|
||||
let s2 = String::from("hello"); // s2 进入作用域
|
||||
|
||||
let s3 = takes_and_gives_back(s2); // s2 被移动到
|
||||
// takes_and_gives_back 中,
|
||||
// takes_and_gives_back 中,
|
||||
// 它也将返回值移给 s3
|
||||
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
|
||||
} // 这里,s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
|
||||
// 所以什么也不会发生。s1 离开作用域并被丢弃
|
||||
|
||||
fn gives_ownership() -> String { // gives_ownership 会将
|
||||
// 返回值移动给
|
||||
// 调用它的函数
|
||||
|
||||
let some_string = String::from("yours"); // some_string 进入作用域.
|
||||
let some_string = String::from("yours"); // some_string 进入作用域。
|
||||
|
||||
some_string // 返回 some_string
|
||||
// 并移出给调用的函数
|
||||
|
@ -16,7 +16,7 @@ fn main() {
|
||||
|
||||
let word = first_word(&s);
|
||||
|
||||
s.clear(); // 错误!
|
||||
s.clear(); // 错误!
|
||||
|
||||
println!("the first word is: {}", word);
|
||||
}
|
||||
|
@ -120,7 +120,7 @@
|
||||
- [高级函数与闭包](ch19-05-advanced-functions-and-closures.md)
|
||||
- [宏](ch19-06-macros.md)
|
||||
|
||||
- [最后的项目: 构建多线程 web server](ch20-00-final-project-a-web-server.md)
|
||||
- [最后的项目:构建多线程 web server](ch20-00-final-project-a-web-server.md)
|
||||
- [建立单线程 web server](ch20-01-single-threaded.md)
|
||||
- [将单线程 server 变为多线程 server](ch20-02-multithreaded.md)
|
||||
- [优雅停机与清理](ch20-03-graceful-shutdown-and-cleanup.md)
|
||||
|
@ -120,7 +120,7 @@
|
||||
- [高级函数与闭包](ch19-05-advanced-functions-and-closures.md)
|
||||
- [宏](ch19-06-macros.md)
|
||||
|
||||
- [最后的项目: 构建多线程 web server](ch20-00-final-project-a-web-server.md)
|
||||
- [最后的项目:构建多线程 web server](ch20-00-final-project-a-web-server.md)
|
||||
- [建立单线程 web server](ch20-01-single-threaded.md)
|
||||
- [将单线程 server 变为多线程 server](ch20-02-multithreaded.md)
|
||||
- [优雅停机与清理](ch20-03-graceful-shutdown-and-cleanup.md)
|
||||
|
@ -97,7 +97,7 @@ error: expected identifier, found keyword `match`
|
||||
|
||||
该错误表示你不能将关键字 `match` 用作函数标识符。你可以使用原始标识符将 `match` 作为函数名称使用:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn r#match(needle: &str, haystack: &str) -> bool {
|
||||
|
@ -41,7 +41,7 @@
|
||||
| `..=` | `..=expr`, `expr..=expr` | 右闭区间范围模式 | `PartialOrd` |
|
||||
| `..` | `..expr` | 结构体更新语法 | |
|
||||
| `..` | `variant(x, ..)`, `struct_type { x, .. }` | “与剩余部分” 的模式绑定 | |
|
||||
| `...` | `expr...expr` | (Deprecated,请使用 `..=`)在模式中: 闭区间范围模式 | |
|
||||
| `...` | `expr...expr` | (Deprecated,请使用 `..=`)在模式中:闭区间范围模式 | |
|
||||
| `/` | `expr / expr` | 算术除法 | `Div` |
|
||||
| `/=` | `var /= expr` | 算术除法与赋值 | `DivAssign` |
|
||||
| `:` | `pat: type`, `ident: type` | 约束 | |
|
||||
|
@ -86,7 +86,7 @@
|
||||
|
||||
`Default::default` 函数通常结合结构体更新语法一起使用,这在第五章的 [“使用结构体更新语法从其他实例中创建实例”][creating-instances-from-other-instances-with-struct-update-syntax] 部分有讨论。可以自定义一个结构体的一小部分字段而剩余字段则使用 `..Default::default()` 设置为默认值。
|
||||
|
||||
例如,当你在 `Option<T>` 实例上使用 `unwrap_or_default` 方法时,`Default` trait 是必须的。如果 `Option<T>` 是 `None`的话, `unwrap_or_default` 方法将返回存储在 `Option<T>` 中 `T` 类型的 `Default::default` 的结果。
|
||||
例如,当你在 `Option<T>` 实例上使用 `unwrap_or_default` 方法时,`Default` trait 是必须的。如果 `Option<T>` 是 `None`的话,`unwrap_or_default` 方法将返回存储在 `Option<T>` 中 `T` 类型的 `Default::default` 的结果。
|
||||
|
||||
[creating-instances-from-other-instances-with-struct-update-syntax]: ch05-01-defining-structs.html#使用结构体更新语法从其他实例创建实例
|
||||
[stack-only-data-copy]: ch04-01-what-is-ownership.html#只在栈上的数据拷贝
|
||||
|
@ -30,7 +30,7 @@ $ cargo fmt
|
||||
|
||||
如果你编写过 Rust 代码,那么你可能见过那些有很明显修复方式的编译器警告。例如,考虑如下代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn do_something() {}
|
||||
@ -69,7 +69,7 @@ $ cargo fix
|
||||
|
||||
如果再次查看 _src/main.rs_,会发现 `cargo fix` 修改了代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn do_something() {}
|
||||
@ -103,7 +103,7 @@ $ cargo clippy
|
||||
|
||||
例如,如果程序使用了如 pi 这样数学常数的近似值,如下:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -128,7 +128,7 @@ error: approximate value of `f{32, 64}::consts::PI` found. Consider using it dir
|
||||
|
||||
这告诉我们 Rust 定义了更为精确的常量,而如果使用了这些常量程序将更加准确。如下代码就不会导致 `clippy` 产生任何错误或警告:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
@ -97,7 +97,7 @@ $ echo $PATH
|
||||
$ rustup update
|
||||
```
|
||||
|
||||
若要卸载 Rust 和 `rustup`,请在命令行中运行如下卸载脚本:
|
||||
若要卸载 Rust 和 `rustup`,请在命令行中运行如下卸载脚本:
|
||||
|
||||
```console
|
||||
$ rustup self uninstall
|
||||
|
@ -38,7 +38,7 @@ $ cd hello_world
|
||||
|
||||
现在打开刚创建的 *main.rs* 文件,输入示例 1-1 中的代码。
|
||||
|
||||
<span class="filename">文件名: main.rs</span>
|
||||
<span class="filename">文件名:main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
@ -35,7 +35,7 @@ $ cd hello_cargo
|
||||
|
||||
请自行选用文本编辑器打开 *Cargo.toml* 文件。它应该看起来如示例 1-2 所示:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[package]
|
||||
@ -60,7 +60,7 @@ edition = "2021"
|
||||
|
||||
现在打开 *src/main.rs* 看看:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
@ -21,7 +21,7 @@ $ cd guessing_game
|
||||
|
||||
看看生成的 _Cargo.toml_ 文件:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch02-guessing-game-tutorial/no-listing-01-cargo-new/Cargo.toml}}
|
||||
@ -29,7 +29,7 @@ $ cd guessing_game
|
||||
|
||||
正如第一章那样,`cargo new` 生成了一个 “Hello, world!” 程序。查看 _src/main.rs_ 文件:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/no-listing-01-cargo-new/src/main.rs}}
|
||||
@ -49,7 +49,7 @@ $ cd guessing_game
|
||||
|
||||
猜猜看程序的第一部分请求和处理用户输入,并检查输入是否符合预期的格式。首先,允许玩家输入猜测。在 _src/main.rs_ 中输入示例 2-1 中的代码。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:all}}
|
||||
@ -209,7 +209,7 @@ You guessed: 6
|
||||
|
||||
Cargo 对外部 crate 的运用是其真正的亮点所在。在我们使用 `rand` 编写代码之前,需要修改 *Cargo.toml* 文件,引入一个 `rand` 依赖。现在打开这个文件并将下面这一行添加到 `[dependencies]` 片段标题之下。在当前版本下,请确保按照我们这里的方式指定 `rand`,否则本教程中的示例代码可能无法工作。
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch02-guessing-game-tutorial/listing-02-02/Cargo.toml:8:}}
|
||||
@ -294,7 +294,7 @@ rand = "0.9.0"
|
||||
|
||||
让我们开始使用 `rand` 来生成一个猜猜看随机数。下一步是更新 *src/main.rs*,如示例 2-3 所示。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-03/src/main.rs:all}}
|
||||
@ -339,7 +339,7 @@ You guessed: 5
|
||||
|
||||
现在有了用户输入和一个随机数,我们可以比较它们。这个步骤如示例 2-4 所示。注意这段代码还不能通过编译,我们稍后会解释。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-04/src/main.rs:here}}
|
||||
@ -367,7 +367,7 @@ You guessed: 5
|
||||
|
||||
所以我们必须把从输入中读取到的 `String` 转换为一个真正的数字类型,才好与秘密数字进行比较。这可以通过在 `main` 函数体中增加如下代码来实现:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/no-listing-03-convert-string-to-number/src/main.rs:here}}
|
||||
@ -412,7 +412,7 @@ Too big!
|
||||
|
||||
`loop` 关键字创建了一个无限循环。我们会增加循环来给用户更多机会猜数字:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/no-listing-04-looping/src/main.rs:here}}
|
||||
@ -453,7 +453,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
让我们增加一个 `break` 语句,在用户猜对时退出游戏:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/no-listing-05-quitting/src/main.rs:here}}
|
||||
@ -465,7 +465,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
为了进一步改善游戏性,不要在用户输入非数字时崩溃,需要忽略非数字,让用户可以继续猜测。可以通过修改 `guess` 将 `String` 转化为 `u32` 那部分代码来实现,如示例 2-5 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-05/src/main.rs:here}}
|
||||
@ -506,7 +506,7 @@ You win!
|
||||
|
||||
太棒了!再有最后一个小的修改,就能完成猜猜看游戏了:还记得程序依然会打印出秘密数字。在测试时还好,但正式发布时会毁了游戏。删掉打印秘密数字的 `println!`。示例 2-6 为最终代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-06/src/main.rs}}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
接着,在新建的 *variables* 目录,打开 *src/main.rs* 并将代码替换为如下代码,这些代码还不能编译,我们会首次检查到不可变错误(immutability error)。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/src/main.rs}}
|
||||
@ -34,7 +34,7 @@ Rust 编译器保证,如果声明一个值不会变,它就真的不会变,
|
||||
|
||||
例如,让我们将 *src/main.rs* 修改为如下代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/src/main.rs}}
|
||||
@ -74,7 +74,7 @@ const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
|
||||
|
||||
正如在[第二章][comparing-the-guess-to-the-secret-number]猜数字游戏中所讲,我们可以定义一个与之前变量同名的新变量。Rustacean 们称之为第一个变量被第二个 **隐藏(Shadowing)** 了,这意味着当您使用变量的名称时,编译器将看到第二个变量。实际上,第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 `let` 关键字来多次隐藏,如下所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/src/main.rs}}
|
||||
|
@ -77,7 +77,7 @@ Rust 也有两个原生的 **浮点数**(*floating-point numbers*)类型,
|
||||
|
||||
这是一个展示浮点数的实例:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-06-floating-point/src/main.rs}}
|
||||
@ -89,7 +89,7 @@ Rust 也有两个原生的 **浮点数**(*floating-point numbers*)类型,
|
||||
|
||||
Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取余。整数除法会向下舍入到最接近的整数。下面的代码展示了如何在 `let` 语句中使用它们:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-07-numeric-operations/src/main.rs}}
|
||||
@ -101,7 +101,7 @@ Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘
|
||||
|
||||
正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:`true` 和 `false`。Rust 中的布尔类型使用 `bool` 表示。例如:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-08-boolean/src/main.rs}}
|
||||
@ -113,7 +113,7 @@ Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘
|
||||
|
||||
Rust 的 `char` 类型是语言中最原生的字母类型。下面是一些声明 `char` 值的例子:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-09-char/src/main.rs}}
|
||||
@ -131,7 +131,7 @@ Rust的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
||||
|
||||
我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-10-tuples/src/main.rs}}
|
||||
@ -139,7 +139,7 @@ Rust的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
||||
|
||||
`tup` 变量绑定到整个元组上,因为元组是一个单独的复合元素。为了从元组中获取单个值,可以使用模式匹配(pattern matching)来解构(destructure)元组值,像这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-11-destructuring-tuples/src/main.rs}}
|
||||
@ -149,7 +149,7 @@ Rust的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
||||
|
||||
我们也可以使用点号(`.`)后跟值的索引来直接访问它们。例如:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-12-tuple-indexing/src/main.rs}}
|
||||
@ -165,7 +165,7 @@ Rust的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
||||
|
||||
我们将数组的值写成在方括号内,用逗号分隔:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-13-arrays/src/main.rs}}
|
||||
@ -200,7 +200,7 @@ let a = [3; 5];
|
||||
|
||||
数组是可以在栈 (stack) 上分配的已知固定大小的单个内存块。可以使用索引来访问数组的元素,像这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-14-array-indexing/src/main.rs}}
|
||||
@ -212,7 +212,7 @@ let a = [3; 5];
|
||||
|
||||
让我们看看如果我们访问数组结尾之后的元素会发生什么呢?比如你执行以下代码,它使用类似于第 2 章中的猜数字游戏的代码从用户那里获取数组索引:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,panics
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs}}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。这是一个包含函数定义示例的程序:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-16-functions/src/main.rs}}
|
||||
@ -32,7 +32,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
|
||||
在这版 `another_function` 中,我们增加了一个参数:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-17-functions-with-parameters/src/main.rs}}
|
||||
@ -50,7 +50,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
|
||||
当定义多个参数时,使用逗号分隔,像这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-18-functions-with-multiple-parameters/src/main.rs}}
|
||||
@ -75,7 +75,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
|
||||
实际上,我们已经使用过语句和表达式。使用 `let` 关键字创建变量并绑定一个值是一个语句。在列表 3-1 中,`let y = 6;` 是一个语句。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-01/src/main.rs}}
|
||||
@ -87,7 +87,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
|
||||
语句不返回值。因此,不能把 `let` 语句赋值给另一个变量,比如下面的例子尝试做的,会产生一个错误:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-19-statements-vs-expressions/src/main.rs}}
|
||||
@ -103,7 +103,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
|
||||
表达式会计算出一个值,并且你将编写的大部分 Rust 代码是由表达式组成的。考虑一个数学运算,比如 `5 + 6`,这是一个表达式并计算出值 `11`。表达式可以是语句的一部分:在示例 3-1 中,语句 `let y = 6;` 中的 `6` 是一个表达式,它计算出的值是 `6`。函数调用是一个表达式。宏调用是一个表达式。用大括号创建的一个新的块作用域也是一个表达式,例如:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-20-blocks-are-expressions/src/main.rs}}
|
||||
@ -124,7 +124,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
|
||||
函数可以向调用它的代码返回值。我们并不对返回值命名,但要在箭头(`->`)后声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。使用 `return` 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式。这是一个有返回值的函数的例子:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-21-function-return-values/src/main.rs}}
|
||||
@ -146,7 +146,7 @@ let x = 5;
|
||||
|
||||
让我们看看另一个例子:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-22-function-parameter-and-return/src/main.rs}}
|
||||
@ -154,7 +154,7 @@ let x = 5;
|
||||
|
||||
运行代码会打印出 `The value of x is: 6`。但如果在包含 `x + 1` 的行尾加上一个分号,把它从表达式变成语句,我们将看到一个错误。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values/src/main.rs}}
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
注释也可以放在包含代码的行的末尾:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-24-comments-end-of-line/src/main.rs}}
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
不过你更经常看到的是以这种格式使用它们,也就是位于它所解释的代码行的上面一行:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-25-comments-above-line/src/main.rs}}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
在 *projects* 目录新建一个叫做 *branches* 的项目,来学习 `if` 表达式。在 *src/main.rs* 文件中,输入如下内容:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-26-if-true/src/main.rs}}
|
||||
@ -42,7 +42,7 @@
|
||||
|
||||
另外值得注意的是代码中的条件 **必须** 是 `bool` 值。如果条件不是 `bool` 值,我们将得到一个错误。例如,尝试运行以下代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-28-if-condition-must-be-bool/src/main.rs}}
|
||||
@ -56,7 +56,7 @@
|
||||
|
||||
这个错误表明 Rust 期望一个 `bool` 却得到了一个整数。不像 Ruby 或 JavaScript 这样的语言,Rust 并不会尝试自动地将非布尔值转换为布尔值。必须总是显式地使用布尔值作为 `if` 的条件。例如,如果想要 `if` 代码块只在一个数字不等于 `0` 时执行,可以把 `if` 表达式修改成下面这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-29-if-not-equal-0/src/main.rs}}
|
||||
@ -68,7 +68,7 @@
|
||||
|
||||
可以将 `else if` 表达式与 `if` 和 `else` 组合来实现多重条件。例如:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-30-else-if/src/main.rs}}
|
||||
@ -88,7 +88,7 @@
|
||||
|
||||
因为 `if` 是一个表达式,我们可以在 `let` 语句的右侧使用它,例如在示例 3-2 中:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-02/src/main.rs}}
|
||||
@ -104,7 +104,7 @@
|
||||
|
||||
记住,代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。在这个例子中,整个 `if` 表达式的值取决于哪个代码块被执行。这意味着 `if` 的每个分支的可能的返回值都必须是相同类型;在示例 3-2 中,`if` 分支和 `else` 分支的结果都是 `i32` 整型。如果它们的类型不匹配,如下面这个例子,则会出现一个错误:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-31-arms-must-return-same-type/src/main.rs}}
|
||||
@ -130,7 +130,7 @@ Rust 有三种循环:`loop`、`while` 和 `for`。我们每一个都试试。
|
||||
|
||||
作为一个例子,将 *loops* 目录中的 *src/main.rs* 文件修改为如下:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-loop/src/main.rs}}
|
||||
@ -174,7 +174,7 @@ again!
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-5-loop-labels/src/main.rs}}
|
||||
```
|
||||
|
||||
外层循环有一个标签 `counting_up`,它将从 0 数到 2。没有标签的内部循环从 10 向下数到 9。第一个没有指定标签的 `break` 将只退出内层循环。`break 'counting_up;` 语句将退出外层循环。这个代码打印:
|
||||
外层循环有一个标签 `counting_up`,它将从 0 数到 2。没有标签的内部循环从 10 向下数到 9。第一个没有指定标签的 `break` 将只退出内层循环。`break 'counting_up;` 语句将退出外层循环。这个代码打印:
|
||||
|
||||
```console
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-5-loop-labels/output.txt}}
|
||||
@ -186,7 +186,7 @@ again!
|
||||
|
||||
然而,这个模式太常用了,Rust 为此内置了一个语言结构,它被称为 `while` 循环。示例 3-3 使用了 `while`:程序循环三次,每次数字都减一。接着,在循环结束后,打印出另一个信息并退出。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-03/src/main.rs}}
|
||||
@ -200,7 +200,7 @@ again!
|
||||
|
||||
可以使用 `while` 结构来遍历集合中的元素,比如数组。例如,看看示例 3-4。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-04/src/main.rs}}
|
||||
@ -220,7 +220,7 @@ again!
|
||||
|
||||
作为更简洁的替代方案,可以使用 `for` 循环来对一个集合的每个元素执行一些代码。`for` 循环看起来如示例 3-5 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-05/src/main.rs}}
|
||||
@ -236,7 +236,7 @@ again!
|
||||
|
||||
下面是一个使用 `for` 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,`rev`,用来反转 range:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-34-for-range/src/main.rs}}
|
||||
|
@ -227,7 +227,7 @@ Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Co
|
||||
|
||||
将值传递给函数与给变量赋值的原理相似。向函数传递值可能会移动或者复制,就像赋值语句一样。示例 4-3 使用注释展示变量何时进入和离开作用域:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-03/src/main.rs}}
|
||||
@ -241,7 +241,7 @@ Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Co
|
||||
|
||||
返回值也可以转移所有权。示例 4-4 展示了一个返回了某些值的示例,与示例 4-3 一样带有类似的注释。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-04/src/main.rs}}
|
||||
@ -255,7 +255,7 @@ Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Co
|
||||
|
||||
我们可以使用元组来返回多个值,如示例 4-5 所示。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-05/src/main.rs}}
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
下面是如何定义并使用一个(新的)`calculate_length` 函数,它以一个对象的引用作为参数而不是获取值的所有权:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:all}}
|
||||
@ -45,7 +45,7 @@ string data on the heap." src="img/trpl04-05.svg" class="center" />
|
||||
|
||||
如果我们尝试修改借用的变量呢?尝试示例 4-6 中的代码。剧透:这行不通!
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-06/src/main.rs}}
|
||||
@ -65,7 +65,7 @@ string data on the heap." src="img/trpl04-05.svg" class="center" />
|
||||
|
||||
我们通过一个小调整就能修复示例 4-6 代码中的错误,允许我们修改一个借用的值,这就是 **可变引用**(*mutable reference*):
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-09-fixes-listing-04-06/src/main.rs}}
|
||||
@ -75,7 +75,7 @@ string data on the heap." src="img/trpl04-05.svg" class="center" />
|
||||
|
||||
可变引用有一个很大的限制:如果你有一个对该变量的可变引用,你就不能再创建对该变量的引用。这些尝试创建两个 `s` 的可变引用的代码会失败:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/src/main.rs:here}}
|
||||
@ -135,7 +135,7 @@ Rust 在同时使用可变与不可变引用时也采用的类似的规则。这
|
||||
|
||||
让我们尝试创建一个悬垂引用,Rust 会通过一个编译时错误来避免:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/src/main.rs}}
|
||||
@ -156,7 +156,7 @@ for it to be borrowed from
|
||||
|
||||
让我们仔细看看我们的 `dangle` 代码的每一步到底发生了什么:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs:here}}
|
||||
|
@ -16,7 +16,7 @@ fn first_word(s: &String) -> ?
|
||||
|
||||
`first_word` 函数有一个参数 `&String`。因为我们不需要所有权,所以这没有问题。不过应该返回什么呢?我们并没有一个真正获取 **部分** 字符串的办法。不过,我们可以返回单词结尾的索引,结尾由一个空格表示。试试如示例 4-7 中的代码。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:here}}
|
||||
@ -48,7 +48,7 @@ fn first_word(s: &String) -> ?
|
||||
|
||||
现在有了一个找到字符串中第一个单词结尾索引的方法,不过这有一个问题。我们返回了一个独立的 `usize`,不过它只在 `&String` 的上下文中才是一个有意义的数字。换句话说,因为它是一个与 `String` 相分离的值,无法保证将来它仍然有效。考虑一下示例 4-8 中使用了示例 4-7 中 `first_word` 函数的程序。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-08/src/main.rs:here}}
|
||||
@ -123,7 +123,7 @@ let slice = &s[..];
|
||||
|
||||
在记住所有这些知识后,让我们重写 `first_word` 来返回一个 slice。“字符串 slice” 的类型声明写作 `&str`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-18-first-word-slice/src/main.rs:here}}
|
||||
@ -141,7 +141,7 @@ fn second_word(s: &String) -> &str {
|
||||
|
||||
现在我们有了一个不易混淆且直观的 API 了,因为编译器会确保指向 `String` 的引用持续有效。还记得示例 4-8 程序中,那个当我们获取第一个单词结尾的索引后,接着就清除了字符串导致索引就无效的 bug 吗?那些代码在逻辑上是不正确的,但却没有显示任何直接的错误。问题会在之后尝试对空字符串使用第一个单词的索引时出现。slice 就不可能出现这种 bug 并让我们更早的知道出问题了。使用 slice 版本的 `first_word` 会抛出一个编译时错误:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-19-slice-error/src/main.rs:here}}
|
||||
@ -185,7 +185,7 @@ fn first_word(s: &String) -> &str {
|
||||
|
||||
如果有一个字符串 slice,可以直接传递它。如果有一个 `String`,则可以传递整个 `String` 的 slice 或对 `String` 的引用。这种灵活性利用了 *deref coercions* 的优势,这个特性我们将在[“函数和方法的隐式 Deref 强制转换”][deref-coercions]章节中介绍。定义一个获取字符串 slice 而不是 `String` 引用的函数使得我们的 API 更加通用并且不会丢失任何功能:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-09/src/main.rs:usage}}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
定义结构体,需要使用 `struct` 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 **字段**(*field*)。例如,示例 5-1 展示了一个存储用户账号信息的结构体:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-01/src/main.rs:here}}
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
一旦定义了结构体后,为了使用它,通过为每个字段指定具体值来创建这个结构体的 **实例**。创建一个实例需要以结构体的名字开头,接着在大括号中使用 `key: value` 键 - 值对的形式提供字段,其中 key 是字段的名字,value 是需要存储在字段中的数据值。实例中字段的顺序不需要和它们在结构体中声明的顺序一致。换句话说,结构体的定义就像一个类型的通用模板,而实例则会在这个模板中放入特定数据来创建这个类型的值。例如,可以像示例 5-2 这样来声明一个特定的用户:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-02/src/main.rs:here}}
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
为了从结构体中获取某个特定的值,可以使用点号。举个例子,想要用户的邮箱地址,可以用 `user1.email`。如果结构体的实例是可变的,我们可以使用点号并为对应的字段赋值。示例 5-3 展示了如何改变一个可变的 `User` 实例中 `email` 字段的值:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-03/src/main.rs:here}}
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
示例 5-4 显示了一个 `build_user` 函数,它返回一个带有给定的 email 和用户名的 `User` 结构体实例。`active` 字段的值为 `true`,并且 `sign_in_count` 的值为 `1`。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-04/src/main.rs:here}}
|
||||
@ -54,7 +54,7 @@
|
||||
|
||||
因为示例 5-4 中的参数名与字段名都完全相同,我们可以使用 **字段初始化简写语法**(*field init shorthand*)来重写 `build_user`,这样其行为与之前完全相同,不过无需重复 `username` 和 `email` 了,如示例 5-5 所示。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-05/src/main.rs:here}}
|
||||
@ -70,7 +70,7 @@
|
||||
|
||||
首先,示例 5-6 展示了不使用更新语法时,如何在 `user2` 中创建一个新 `User` 实例。我们为 `email` 设置了新的值,其他值则使用了实例 5-2 中创建的 `user1` 中的同名值:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-06/src/main.rs:here}}
|
||||
@ -80,7 +80,7 @@
|
||||
|
||||
使用结构体更新语法,我们可以通过更少的代码来达到相同的效果,如示例 5-7 所示。`..` 语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-07/src/main.rs:here}}
|
||||
@ -98,7 +98,7 @@
|
||||
|
||||
要定义元组结构体,以 `struct` 关键字和结构体名开头并后跟元组中的类型。例如,下面是两个分别叫做 `Color` 和 `Point` 元组结构体的定义和用法:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-01-tuple-structs/src/main.rs}}
|
||||
@ -110,7 +110,7 @@
|
||||
|
||||
我们也可以定义一个没有任何字段的结构体!它们被称为 **类单元结构体**(*unit-like structs*)因为它们类似于 `()`,即[“元组类型”][tuples]一节中提到的 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。我们将在第十章介绍 trait。下面是一个声明和实例化一个名为 `AlwaysEqual` 的 unit 结构的例子。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-04-unit-like-structs/src/main.rs}}
|
||||
@ -124,7 +124,7 @@
|
||||
>
|
||||
> 可以使结构体存储被其他对象拥有的数据的引用,不过这么做的话需要用上 **生命周期**(*lifetimes*),这是一个第十章会讨论的 Rust 功能。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的,比如这样:
|
||||
>
|
||||
> <span class="filename">文件名: src/main.rs</span>
|
||||
> <span class="filename">文件名:src/main.rs</span>
|
||||
>
|
||||
> ```rust,ignore,does_not_compile
|
||||
> struct User {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
使用 Cargo 新建一个叫做 *rectangles* 的二进制程序,它获取以像素为单位的长方形的宽度和高度,并计算出长方形的面积。示例 5-8 显示了位于项目的 *src/main.rs* 中的小程序,它刚刚好实现此功能:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/src/main.rs:all}}
|
||||
@ -36,7 +36,7 @@
|
||||
|
||||
示例 5-9 展示了使用元组的另一个程序版本。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-09/src/main.rs}}
|
||||
@ -52,7 +52,7 @@
|
||||
|
||||
我们使用结构体为数据命名来为其赋予意义。我们可以将我们正在使用的元组转换成一个有整体名称而且每个部分也有对应名字的结构体,如示例 5-10 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-10/src/main.rs}}
|
||||
@ -70,7 +70,7 @@
|
||||
|
||||
在调试程序时打印出 `Rectangle` 实例来查看其所有字段的值非常有用。示例 5-11 像前面章节那样尝试使用 [`println!` 宏][println]。但这并不行。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/src/main.rs}}
|
||||
@ -108,7 +108,7 @@
|
||||
|
||||
Rust **确实** 包含了打印出调试信息的功能,不过我们必须为结构体显式选择这个功能。为此,在结构体定义之前加上外部属性 `#[derive(Debug)]`,如示例 5-12 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/src/main.rs}}
|
||||
@ -144,7 +144,7 @@ Rust **确实** 包含了打印出调试信息的功能,不过我们必须为
|
||||
{{#include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/output.txt}}
|
||||
```
|
||||
|
||||
我们可以看到第一点输出来自 *src/main.rs* 第 10 行,我们正在调试表达式 `30 * scale`,其结果值是 `60`(为整数实现的 `Debug` 格式化是只打印它们的值)。在 *src/main.rs* 第 14行 的 `dbg!` 调用输出 `&rect1` 的值,即 `Rectangle` 结构。这个输出使用了更为易读的 `Debug` 格式。当你试图弄清楚你的代码在做什么时,`dbg!` 宏可能真的很有帮助!
|
||||
我们可以看到第一点输出来自 *src/main.rs* 第 10 行,我们正在调试表达式 `30 * scale`,其结果值是 `60`(为整数实现的 `Debug` 格式化是只打印它们的值)。在 *src/main.rs* 第 14 行 的 `dbg!` 调用输出 `&rect1` 的值,即 `Rectangle` 结构。这个输出使用了更为易读的 `Debug` 格式。当你试图弄清楚你的代码在做什么时,`dbg!` 宏可能真的很有帮助!
|
||||
|
||||
除了 `Debug` trait,Rust 还为我们提供了很多可以通过 `derive` 属性来使用的 trait,他们可以为我们的自定义类型增加实用的行为。[附录 C][app-c] 中列出了这些 trait 和行为。第十章会介绍如何通过自定义行为来实现这些 trait,同时还有如何创建你自己的 trait。除了 `derive` 之外,还有很多属性;更多信息请参见 [Rust Reference][attributes] 的 Attributes 部分。
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
让我们把前面实现的获取一个 `Rectangle` 实例作为参数的 `area` 函数,改写成一个定义于 `Rectangle` 结构体上的 `area` 方法,如示例 5-13 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-13/src/main.rs}}
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
请注意,我们可以选择将方法的名称与结构中的一个字段相同。例如,我们可以在 `Rectangle` 上定义一个方法,并命名为 `width`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-06-method-field-interaction/src/main.rs:here}}
|
||||
@ -73,7 +73,7 @@
|
||||
|
||||
让我们通过实现 `Rectangle` 结构体上的另一方法来练习使用方法。这回,我们让一个 `Rectangle` 的实例获取另一个 `Rectangle` 实例,如果 `self` (第一个 `Rectangle`)能完全包含第二个长方形则返回 `true`;否则返回 `false`。一旦我们定义了 `can_hold` 方法,就可以编写示例 5-14 中的代码。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-14/src/main.rs}}
|
||||
@ -90,7 +90,7 @@ Can rect1 hold rect3? false
|
||||
|
||||
因为我们想定义一个方法,所以它应该位于 `impl Rectangle` 块中。方法名是 `can_hold`,并且它会获取另一个 `Rectangle` 的不可变借用作为参数。通过观察调用方法的代码可以看出参数是什么类型的:`rect1.can_hold(&rect2)` 传入了 `&rect2`,它是一个 `Rectangle` 的实例 `rect2` 的不可变借用。这是可以理解的,因为我们只需要读取 `rect2`(而不是写入,这意味着我们需要一个不可变借用),而且希望 `main` 保持 `rect2` 的所有权,这样就可以在调用这个方法后继续使用它。`can_hold` 的返回值是一个布尔值,其实现会分别检查 `self` 的宽高是否都大于另一个 `Rectangle`。让我们在示例 5-13 的 `impl` 块中增加这个新的 `can_hold` 方法,如示例 5-15 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-15/src/main.rs:here}}
|
||||
@ -106,7 +106,7 @@ Can rect1 hold rect3? false
|
||||
|
||||
不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。这些函数的名称通常为 `new` ,但 `new` 并不是一个关键字。例如我们可以提供一个叫做 `square` 关联函数,它接受一个维度参数并且同时作为宽和高,这样可以更轻松的创建一个正方形 `Rectangle` 而不必指定两次同样的值:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-03-associated-functions/src/main.rs:here}}
|
||||
|
@ -31,7 +31,7 @@ $ ls my-project/src
|
||||
main.rs
|
||||
```
|
||||
|
||||
运行了这条命令后,我们先用 `ls` (译者注:此命令为Linux平台的指令,Windows下可用dir)来看看 Cargo 给我们创建了什么,Cargo 会给我们的包创建一个 *Cargo.toml* 文件。查看 *Cargo.toml* 的内容,会发现并没有提到 *src/main.rs*,因为 Cargo 遵循的一个约定:*src/main.rs* 就是一个与包同名的二进制 crate 的 crate 根。同样的,Cargo 知道如果包目录中包含 *src/lib.rs*,则包带有与其同名的库 crate,且 *src/lib.rs* 是 crate 根。crate 根文件将由 Cargo 传递给 `rustc` 来实际构建库或者二进制项目。
|
||||
运行了这条命令后,我们先用 `ls` (译者注:此命令为 Linux 平台的指令,Windows 下可用 dir)来看看 Cargo 给我们创建了什么,Cargo 会给我们的包创建一个 *Cargo.toml* 文件。查看 *Cargo.toml* 的内容,会发现并没有提到 *src/main.rs*,因为 Cargo 遵循的一个约定:*src/main.rs* 就是一个与包同名的二进制 crate 的 crate 根。同样的,Cargo 知道如果包目录中包含 *src/lib.rs*,则包带有与其同名的库 crate,且 *src/lib.rs* 是 crate 根。crate 根文件将由 Cargo 传递给 `rustc` 来实际构建库或者二进制项目。
|
||||
|
||||
在此,我们有了一个只包含 *src/main.rs* 的包,意味着它只含有一个名为 `my-project` 的二进制 crate。如果一个包同时含有 *src/main.rs* 和 *src/lib.rs*,则它有两个 crate:一个二进制的和一个库的,且名字都与包相同。通过将文件放在 *src/bin* 目录下,一个包可以拥有多个二进制 crate:每个 *src/bin* 下的文件都会被编译成一个独立的二进制 crate。
|
||||
|
||||
|
@ -18,12 +18,12 @@
|
||||
- 在文件 *src/garden.rs*
|
||||
- 在文件 *src/garden/mod.rs*
|
||||
- **声明子模块**: 在除了 crate 根节点以外的其他文件中,你可以定义子模块。比如,你可能在*src/garden.rs*中定义了`mod vegetables;`。编译器会在以父模块命名的目录中寻找子模块代码:
|
||||
- 内联, 在大括号中,当`mod vegetables`后方不是一个分号而是一个大括号
|
||||
- 内联,在大括号中,当`mod vegetables`后方不是一个分号而是一个大括号
|
||||
- 在文件 *src/garden/vegetables.rs*
|
||||
- 在文件 *src/garden/vegetables/mod.rs*
|
||||
- **模块中的代码路径**: 一旦一个模块是你 crate 的一部分,你可以在隐私规则允许的前提下,从同一个 crate 内的任意地方,通过代码路径引用该模块的代码。举例而言,一个 garden vegetables 模块下的`Asparagus`类型可以在`crate::garden::vegetables::Asparagus`被找到。
|
||||
- **私有 vs 公用**: 一个模块里的代码默认对其父模块私有。为了使一个模块公用,应当在声明时使用`pub mod`替代`mod`。为了使一个公用模块内部的成员公用,应当在声明前使用`pub`。
|
||||
- **`use` 关键字**: 在一个作用域内,`use`关键字创建了一个成员的快捷方式,用来减少长路径的重复。在任何可以引用`crate::garden::vegetables::Asparagus`的作用域, 你可以通过 `use crate::garden::vegetables::Asparagus;`创建一个快捷方式,然后你就可以在作用域中只写`Asparagus`来使用该类型。
|
||||
- **`use` 关键字**: 在一个作用域内,`use`关键字创建了一个成员的快捷方式,用来减少长路径的重复。在任何可以引用`crate::garden::vegetables::Asparagus`的作用域,你可以通过 `use crate::garden::vegetables::Asparagus;`创建一个快捷方式,然后你就可以在作用域中只写`Asparagus`来使用该类型。
|
||||
|
||||
这里我们创建一个名为`backyard`的二进制 crate 来说明这些规则。该 crate 的路径同样命名为`backyard`,该路径包含了这些文件和目录:
|
||||
|
||||
@ -40,21 +40,21 @@ backyard
|
||||
|
||||
这个例子中的 crate 根文件是*src/main.rs*,该文件包括了:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,noplayground,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/quick-reference-example/src/main.rs}}
|
||||
```
|
||||
|
||||
`pub mod garden;`行告诉编译器应该包含在*src/garden.rs*文件中发现的代码:
|
||||
`pub mod garden;`行告诉编译器应该包含在*src/garden.rs*文件中发现的代码:
|
||||
|
||||
<span class="filename">文件名: src/garden.rs</span>
|
||||
<span class="filename">文件名:src/garden.rs</span>
|
||||
|
||||
```rust,noplayground,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/quick-reference-example/src/garden.rs}}
|
||||
```
|
||||
|
||||
在此处, `pub mod vegetables;`意味着在*src/garden/vegetables.rs*中的代码也应该被包括。这些代码是:
|
||||
在此处, `pub mod vegetables;`意味着在*src/garden/vegetables.rs*中的代码也应该被包括。这些代码是:
|
||||
|
||||
```rust,noplayground,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/quick-reference-example/src/garden/vegetables.rs}}
|
||||
@ -70,7 +70,7 @@ backyard
|
||||
|
||||
我们可以将函数放置到嵌套的模块中,来使我们的 crate 结构与实际的餐厅结构相同。通过执行 `cargo new --lib restaurant`,来创建一个新的名为 `restaurant` 的库。然后将示例 7-1 中所罗列出来的代码放入 *src/lib.rs* 中,来定义一些模块和函数。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-01/src/lib.rs}}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
让我们回到示例 7-1。我们如何调用 `add_to_waitlist` 函数?还是同样的问题,`add_to_waitlist` 函数的路径是什么?在示例 7-3 中,我们通过删除一些模块和函数,稍微简化了一下我们的代码。我们在 crate 根定义了一个新函数 `eat_at_restaurant`,并在其中展示调用 `add_to_waitlist` 函数的两种方法。`eat_at_restaurant` 函数是我们 crate 库的一个公共 API,所以我们使用 `pub` 关键字来标记它。在 [“使用`pub`关键字暴露路径”][pub] 一节,我们将详细介绍 `pub`。注意,这个例子无法编译通过,我们稍后会解释原因。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-03/src/lib.rs}}
|
||||
@ -50,7 +50,7 @@ Rust 选择以这种方式来实现模块系统功能,因此默认隐藏内部
|
||||
|
||||
让我们回头看一下示例 7-4 的错误,它告诉我们 `hosting` 模块是私有的。我们想让父模块中的 `eat_at_restaurant` 函数可以访问子模块中的 `add_to_waitlist` 函数,因此我们使用 `pub` 关键字来标记 `hosting` 模块,如示例 7-5 所示。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs}}
|
||||
@ -72,7 +72,7 @@ Rust 选择以这种方式来实现模块系统功能,因此默认隐藏内部
|
||||
|
||||
让我们继续将 `pub` 关键字放置在 `add_to_waitlist` 函数的定义之前,使其变成公有。如示例 7-7 所示。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground,test_harness
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs}}
|
||||
@ -94,7 +94,7 @@ Rust 选择以这种方式来实现模块系统功能,因此默认隐藏内部
|
||||
|
||||
考虑一下示例 7-8 中的代码,它模拟了厨师更正了一个错误订单,并亲自将其提供给客户的情况。`fix_incorrect_order` 函数通过指定的 `super` 起始的 `serve_order` 路径,来调用 `serve_order` 函数:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground,test_harness
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-08/src/lib.rs}}
|
||||
@ -108,7 +108,7 @@ Rust 选择以这种方式来实现模块系统功能,因此默认隐藏内部
|
||||
|
||||
我们还可以使用 `pub` 来设计公有的结构体和枚举,不过有一些额外的细节需要注意。如果我们在一个结构体定义的前面使用了 `pub` ,这个结构体会变成公有的,但是这个结构体的字段仍然是私有的。我们可以根据情况决定每个字段是否公有。在示例 7-9 中,我们定义了一个公有结构体 `back_of_house:Breakfast`,其中有一个公有字段 `toast` 和私有字段 `seasonal_fruit`。这个例子模拟的情况是,在一家餐馆中,顾客可以选择随餐附赠的面包类型,但是厨师会根据季节和库存情况来决定随餐搭配的水果。餐馆可用的水果变化是很快的,所以顾客不能选择水果,甚至无法看到他们将会得到什么水果。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-09/src/lib.rs}}
|
||||
@ -122,7 +122,7 @@ Rust 选择以这种方式来实现模块系统功能,因此默认隐藏内部
|
||||
|
||||
与之相反,如果我们将枚举设为公有,则它的所有成员都将变为公有。我们只需要在 `enum` 关键字前面加上 `pub`,就像示例 7-10 展示的那样。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-10/src/lib.rs}}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
在示例 7-11 中,我们将 `crate::front_of_house::hosting` 模块引入了 `eat_at_restaurant` 函数的作用域,而我们只需要指定 `hosting::add_to_waitlist` 即可在 `eat_at_restaurant` 中调用 `add_to_waitlist` 函数。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground,test_harness
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-11/src/lib.rs}}
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
你还可以使用 `use` 和相对路径来将一个项引入作用域。示例 7-12 展示了如何指定相对路径来取得与示例 7-11 中一样的行为。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground,test_harness
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-12/src/lib.rs}}
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
在示例 7-11 中,你可能会比较疑惑,为什么我们是指定 `use crate::front_of_house::hosting` ,然后在 `eat_at_restaurant` 中调用 `hosting::add_to_waitlist` ,而不是通过指定一直到 `add_to_waitlist` 函数的 `use` 路径来得到相同的结果,如示例 7-13 所示。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground,test_harness
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-13/src/lib.rs}}
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
另一方面,使用 `use` 引入结构体、枚举和其他项时,习惯是指定它们的完整路径。示例 7-14 展示了将 `HashMap` 结构体引入二进制 crate 作用域的习惯用法。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-14/src/main.rs}}
|
||||
@ -56,7 +56,7 @@
|
||||
|
||||
这个习惯用法有一个例外,那就是我们想使用 `use` 语句将两个具有相同名称的项带入作用域,因为 Rust 不允许这样做。示例 7-15 展示了如何将两个具有相同名称但不同父模块的 `Result` 类型引入作用域,以及如何引用它们。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-15/src/lib.rs:here}}
|
||||
@ -70,7 +70,7 @@
|
||||
|
||||
使用 `use` 将两个同名类型引入同一作用域这个问题还有另一个解决办法:在这个类型的路径后面,我们使用 `as` 指定一个新的本地名称或者别名。示例 7-16 展示了另一个编写示例 7-15 中代码的方法,通过 `as` 重命名其中一个 `Result` 类型。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-16/src/lib.rs:here}}
|
||||
@ -78,7 +78,7 @@
|
||||
|
||||
<span class="caption">示例 7-16: 使用 `as` 关键字重命名引入作用域的类型</span>
|
||||
|
||||
在第二个 `use` 语句中,我们选择 `IoResult` 作为 `std::io::Result` 的新名称,它与从 `std::fmt` 引入作用域的 `Result` 并不冲突。示例 7-15 和示例 7-16 都是惯用的,如何选择都取决于你!
|
||||
在第二个 `use` 语句中,我们选择 `IoResult` 作为 `std::io::Result` 的新名称,它与从 `std::fmt` 引入作用域的 `Result` 并不冲突。示例 7-15 和示例 7-16 都是惯用的,如何选择都取决于你!
|
||||
|
||||
### 使用 `pub use` 重导出名称
|
||||
|
||||
@ -86,7 +86,7 @@
|
||||
|
||||
示例 7-17 将示例 7-11 根模块中的 `use` 改为 `pub use` 。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground,test_harness
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-17/src/lib.rs}}
|
||||
@ -102,7 +102,7 @@
|
||||
|
||||
在第二章中我们编写了一个猜猜看游戏。那个项目使用了一个外部包,`rand`,来生成随机数。为了在项目中使用 `rand`,在 *Cargo.toml* 中加入了如下行:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch02-guessing-game-tutorial/listing-02-02/Cargo.toml:9:}}
|
||||
@ -130,7 +130,7 @@ use std::collections::HashMap;
|
||||
|
||||
当需要引入很多定义于相同包或相同模块的项时,为每一项单独列出一行会占用源码很大的空间。例如猜猜看章节示例 2-4 中有两行 `use` 语句都从 `std` 引入项到作用域:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-01-use-std-unnested/src/main.rs:here}}
|
||||
@ -138,7 +138,7 @@ use std::collections::HashMap;
|
||||
|
||||
相反,我们可以使用嵌套路径将相同的项在一行中引入作用域。这么做需要指定路径的相同部分,接着是两个冒号,接着是大括号中的各自不同的路径部分,如示例 7-18 所示。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-18/src/main.rs:here}}
|
||||
@ -150,7 +150,7 @@ use std::collections::HashMap;
|
||||
|
||||
我们可以在路径的任何层级使用嵌套路径,这在组合两个共享子路径的 `use` 语句时非常有用。例如,示例 7-19 中展示了两个 `use` 语句:一个将 `std::io` 引入作用域,另一个将 `std::io::Write` 引入作用域:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-19/src/lib.rs}}
|
||||
@ -160,7 +160,7 @@ use std::collections::HashMap;
|
||||
|
||||
两个路径的相同部分是 `std::io`,这正是第一个路径。为了在一行 `use` 语句中引入这两个路径,可以在嵌套路径中使用 `self`,如示例 7-20 所示。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-20/src/lib.rs}}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
例如,我们从示例 7-17 开始,将 `front_of_house` 模块移动到属于它自己的文件 *src/front_of_house.rs* 中,通过改变 crate 根文件,使其包含示例 7-21 所示的代码。在这个例子中,crate 根文件是 *src/lib.rs*,这也同样适用于以 *src/main.rs* 为 crate 根文件的二进制 crate 项。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-21-and-22/src/lib.rs}}
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
*src/front_of_house.rs* 会获取 `front_of_house` 模块的定义内容,如示例 7-22 所示。
|
||||
|
||||
<span class="filename">文件名: src/front_of_house.rs</span>
|
||||
<span class="filename">文件名:src/front_of_house.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-21-and-22/src/front_of_house.rs}}
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
在 `mod front_of_house` 后使用分号,而不是代码块,这将告诉 Rust 在另一个与模块同名的文件中加载模块的内容。继续重构我们例子,将 `hosting` 模块也提取到其自己的文件中,仅对 *src/front_of_house.rs* 包含 `hosting` 模块的声明进行修改:
|
||||
|
||||
<span class="filename">文件名: src/front_of_house.rs</span>
|
||||
<span class="filename">文件名:src/front_of_house.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-02-extracting-hosting/src/front_of_house.rs}}
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
接着我们创建一个 *src/front_of_house* 目录和一个包含 `hosting` 模块定义的 *src/front_of_house/hosting.rs* 文件:
|
||||
|
||||
<span class="filename">文件名: src/front_of_house/hosting.rs</span>
|
||||
<span class="filename">文件名:src/front_of_house/hosting.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-02-extracting-hosting/src/front_of_house/hosting.rs}}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
让我们在一个简单的程序中调用 `panic!`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic,panics
|
||||
{{#rustdoc_include ../listings/ch09-error-handling/no-listing-01-panic/src/main.rs}}
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
让我们来看看另一个因为我们代码中的 bug 引起的别的库中 `panic!` 的例子,而不是直接的宏调用。示例 9-1 有一些尝试通过索引访问 vector 中元素的例子:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic,panics
|
||||
{{#rustdoc_include ../listings/ch09-error-handling/listing-09-01/src/main.rs}}
|
||||
|
@ -19,7 +19,7 @@ enum Result<T, E> {
|
||||
|
||||
让我们调用一个返回 `Result` 的函数,因为它可能会失败:如示例 9-3 所示打开一个文件:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch09-error-handling/listing-09-03/src/main.rs}}
|
||||
@ -47,7 +47,7 @@ enum Result<T, E> {
|
||||
|
||||
我们需要在示例 9-3 的代码中增加根据 `File::open` 返回值进行不同处理的逻辑。示例 9-4 展示了一个使用基本工具处理 `Result` 的例子:第六章学习过的 `match` 表达式。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic
|
||||
{{#rustdoc_include ../listings/ch09-error-handling/listing-09-04/src/main.rs}}
|
||||
@ -72,7 +72,7 @@ enum Result<T, E> {
|
||||
|
||||
示例 9-4 中的代码不管 `File::open` 是因为什么原因失败都会 `panic!`。我们真正希望的是对不同的错误原因采取不同的行为:如果 `File::open `因为文件不存在而失败,我们希望创建这个文件并返回新文件的句柄。如果 `File::open` 因为任何其他原因失败,例如没有打开文件的权限,我们仍然希望像示例 9-4 那样 `panic!`。让我们看看示例 9-5,其中 `match` 增加了另一个分支:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch09-error-handling/listing-09-05/src/main.rs}}
|
||||
@ -113,7 +113,7 @@ enum Result<T, E> {
|
||||
|
||||
`match` 能够胜任它的工作,不过它可能有点冗长并且不总是能很好的表明其意图。`Result<T, E>` 类型定义了很多辅助方法来处理各种情况。其中之一叫做 `unwrap`,它的实现就类似于示例 9-4 中的 `match` 语句。如果 `Result` 值是成员 `Ok`,`unwrap` 会返回 `Ok` 中的值。如果 `Result` 是成员 `Err`,`unwrap` 会为我们调用 `panic!`。这里是一个实践 `unwrap` 的例子:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic
|
||||
{{#rustdoc_include ../listings/ch09-error-handling/no-listing-04-unwrap/src/main.rs}}
|
||||
@ -129,7 +129,7 @@ src/libcore/result.rs:906:4
|
||||
|
||||
还有另一个类似于 `unwrap` 的方法它还允许我们选择 `panic!` 的错误信息:`expect`。使用 `expect` 而不是 `unwrap` 并提供一个好的错误信息可以表明你的意图并更易于追踪 panic 的根源。`expect` 的语法看起来像这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic
|
||||
{{#rustdoc_include ../listings/ch09-error-handling/no-listing-05-expect/src/main.rs}}
|
||||
@ -150,7 +150,7 @@ thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
||||
|
||||
例如,示例 9-6 展示了一个从文件中读取用户名的函数。如果文件不存在或不能读取,这个函数会将这些错误返回给调用它的代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#include ../listings/ch09-error-handling/listing-09-06/src/main.rs:here}}
|
||||
@ -172,7 +172,7 @@ thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
||||
|
||||
示例 9-7 展示了一个 `read_username_from_file` 的实现,它实现了与示例 9-6 中的代码相同的功能,不过这个实现使用了 `?` 运算符:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#include ../listings/ch09-error-handling/listing-09-07/src/main.rs:here}}
|
||||
@ -188,7 +188,7 @@ thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
||||
|
||||
`?` 运算符消除了大量样板代码并使得函数的实现更简单。我们甚至可以在 `?` 之后直接使用链式方法调用来进一步缩短代码,如示例 9-8 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#include ../listings/ch09-error-handling/listing-09-08/src/main.rs:here}}
|
||||
@ -200,7 +200,7 @@ thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
||||
|
||||
说到编写这个函数的不同方法,甚至还有一个更短的写法:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#include ../listings/ch09-error-handling/listing-09-09/src/main.rs:here}}
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
考虑一下这个寻找列表中最大值的小程序,如示例 10-1 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-01/src/main.rs:here}}
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
如果需要在两个不同的列表中寻找最大值,我们可以重复示例 10-1 中的代码,这样程序中就会存在两段相同逻辑的代码,如示例 10-2 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-02/src/main.rs}}
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
在示例 10-3 的程序中将寻找最大值的代码提取到了一个叫做 `largest` 的函数中。这不同于示例 10-1 中的代码只能在一个特定的列表中找到最大的数字,这个程序可以在两个不同的列表中找到最大的数字。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-03/src/main.rs:here}}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
回到 `largest` 函数,示例 10-4 中展示了两个函数,它们的功能都是寻找 slice 中最大值。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-04/src/main.rs:here}}
|
||||
@ -34,7 +34,7 @@ fn largest<T>(list: &[T]) -> T {
|
||||
|
||||
示例 10-5 中的 `largest` 函数在它的签名中使用了泛型,统一了两个实现。该示例也展示了如何调用 `largest` 函数,把 `i32` 值的 slice 或 `char` 值的 slice 传给它。请注意这些代码还不能编译,不过稍后在本章会解决这个问题。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-05/src/main.rs}}
|
||||
@ -56,7 +56,7 @@ fn largest<T>(list: &[T]) -> T {
|
||||
|
||||
同样也可以用 `<>` 语法来定义结构体,它包含一个或多个泛型参数类型字段。示例 10-6 展示了如何定义和使用一个可以存放任何类型的 `x` 和 `y` 坐标值的结构体 `Point`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-06/src/main.rs}}
|
||||
@ -68,7 +68,7 @@ fn largest<T>(list: &[T]) -> T {
|
||||
|
||||
注意 `Point<T>` 的定义中只使用了一个泛型类型,这个定义表明结构体 `Point<T>` 对于一些类型 `T` 是泛型的,而且字段 `x` 和 `y` **都是** 相同类型的,无论它具体是何类型。如果尝试创建一个有不同类型值的 `Point<T>` 的实例,像示例 10-7 中的代码就不能编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-07/src/main.rs}}
|
||||
@ -84,7 +84,7 @@ fn largest<T>(list: &[T]) -> T {
|
||||
|
||||
如果想要定义一个 `x` 和 `y` 可以有不同类型且仍然是泛型的 `Point` 结构体,我们可以使用多个泛型类型参数。在示例 10-8 中,我们修改 `Point` 的定义为拥有两个泛型类型 `T` 和 `U`。其中字段 `x` 是 `T` 类型的,而字段 `y` 是 `U` 类型的:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-08/src/main.rs}}
|
||||
@ -124,7 +124,7 @@ enum Result<T, E> {
|
||||
|
||||
在为结构体和枚举实现方法时(像第五章那样),一样也可以用泛型。示例 10-9 中展示了示例 10-6 中定义的结构体 `Point<T>`,和在其上实现的名为 `x` 的方法。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-09/src/main.rs}}
|
||||
@ -148,7 +148,7 @@ enum Result<T, E> {
|
||||
|
||||
结构体定义中的泛型类型参数并不总是与结构体方法签名中使用的泛型是同一类型。示例 10-11 中为 `Point` 结构体使用了泛型类型 `X1` 和 `Y1`,为 `mixup` 方法签名使用了 `X2` 和 `Y2` 来使得示例更加清楚。这个方法用 `self` 的 `Point` 类型的 `x` 值(类型 `X1`)和参数的 `Point` 类型的 `y` 值(类型 `Y2`)来创建一个新 `Point` 类型的实例:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-11/src/main.rs}}
|
||||
@ -179,7 +179,7 @@ let float = Some(5.0);
|
||||
|
||||
编译器生成的单态化版本的代码看起来像这样,并包含将泛型 `Option<T>` 替换为编译器创建的具体定义后的用例代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
enum Option_i32 {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
我们想要创建一个名为 `aggregator` 的多媒体聚合库用来显示可能储存在 `NewsArticle` 或 `Tweet` 实例中的数据的总结。每一个结构体都需要的行为是他们是能够被总结的,这样的话就可以调用实例的 `summarize` 方法来请求总结。示例 10-12 中展示了一个表现这个概念的公有 `Summary` trait 的定义:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-12/src/lib.rs}}
|
||||
@ -34,7 +34,7 @@ trait 体中可以有多个方法:一行一个方法签名且都以分号结
|
||||
|
||||
现在我们定义了 `Summary` trait 的签名,接着就可以在多媒体聚合库中实现这个类型了。示例 10-13 中展示了 `NewsArticle` 结构体上 `Summary` trait 的一个实现,它使用标题、作者和创建的位置作为 `summarize` 的返回值。对于 `Tweet` 结构体,我们选择将 `summarize` 定义为用户名后跟推文的全部文本作为返回值,并假设推文内容已经被限制为 280 字符以内。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-13/src/lib.rs:here}}
|
||||
@ -62,7 +62,7 @@ trait 体中可以有多个方法:一行一个方法签名且都以分号结
|
||||
|
||||
示例 10-14 中展示了如何为 `Summary` trait 的 `summarize` 方法指定一个默认的字符串值,而不是像示例 10-12 中那样只是定义方法签名:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-14/src/lib.rs:here}}
|
||||
@ -221,7 +221,7 @@ fn some_function<T, U>(t: &T, u: &U) -> i32
|
||||
|
||||
为了只对实现了 `Copy` 的类型调用这些代码,可以在 `T` 的 trait bounds 中增加 `Copy`!示例 10-15 中展示了一个可以编译的泛型版本的 `largest` 函数的完整代码,只要传递给 `largest` 的 slice 值的类型实现了 `PartialOrd` **和** `Copy` 这两个 trait,例如 `i32` 和 `char`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-15/src/main.rs}}
|
||||
|
@ -57,7 +57,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
让我们来编写一个返回两个字符串 slice 中较长者的函数。这个函数获取两个字符串 slice 并返回一个字符串 slice。一旦我们实现了 `longest` 函数,示例 10-20 中的代码应该会打印出 `The longest string is abcd`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-20/src/main.rs}}
|
||||
@ -69,7 +69,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
如果尝试像示例 10-21 中那样实现 `longest` 函数,它并不能编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-21/src/main.rs:here}}
|
||||
@ -108,7 +108,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
现在来看看 `longest` 函数的上下文中的生命周期。就像泛型类型参数,泛型生命周期参数需要声明在函数名和参数列表间的尖括号中。
|
||||
在这个签名中我们想要表达的限制是所有(两个)参数和返回的引用的生命周期是相关的,也就是这两个参数和返回的引用存活的一样久。就像示例 10-22 中在每个引用中都加上了 `'a` 那样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-22/src/main.rs:here}}
|
||||
@ -129,7 +129,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
让我们看看如何通过传递拥有不同具体生命周期的引用来限制 `longest` 函数的使用。示例 10-23 是一个很直观的例子。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-23/src/main.rs:here}}
|
||||
@ -141,7 +141,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
接下来,让我们尝试另外一个例子,该例子揭示了 `result` 的引用的生命周期必须是两个参数中较短的那个。以下代码将 `result` 变量的声明移动出内部作用域,但是将 `result` 和 `string2` 变量的赋值语句一同留在内部作用域中。接着,使用了变量 `result` 的 `println!` 也被移动到内部作用域之外。注意示例 10-24 中的代码不能通过编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-24/src/main.rs:here}}
|
||||
@ -165,7 +165,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
指定生命周期参数的正确方式依赖函数实现的具体功能。例如,如果将 `longest` 函数的实现修改为总是返回第一个参数而不是最长的字符串 slice,就不需要为参数 `y` 指定一个生命周期。如下代码将能够编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-08-only-one-reference-with-lifetime/src/main.rs:here}}
|
||||
@ -175,7 +175,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
当从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配。如果返回的引用 **没有** 指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值,它将会是一个悬垂引用,因为它将会在函数结束时离开作用域。尝试考虑这个并不能编译的 `longest` 函数实现:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-09-unrelated-lifetime/src/main.rs:here}}
|
||||
@ -195,7 +195,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
目前为止,我们只定义过有所有权类型的结构体。接下来,我们将定义包含引用的结构体,不过这需要为结构体定义中的每一个引用添加生命周期注解。示例 10-25 中有一个存放了一个字符串 slice 的结构体 `ImportantExcerpt`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-25/src/main.rs}}
|
||||
@ -211,7 +211,7 @@ Rust 编译器有一个 **借用检查器**(*borrow checker*),它比较作
|
||||
|
||||
现在我们已经知道了每一个引用都有一个生命周期,而且我们需要为那些使用了引用的函数或结构体指定生命周期。然而,第四章的示例 4-9 中有一个函数,如示例 10-26 所示,它没有生命周期注解却能编译成功:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-26/src/main.rs:here}}
|
||||
|
@ -30,7 +30,7 @@ $ cd adder
|
||||
|
||||
adder 库中 `src/lib.rs` 的内容应该看起来如示例 11-1 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}}
|
||||
@ -62,7 +62,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
让我们改变测试的名称并看看这如何改变测试的输出。给 `it_works` 函数起个不同的名字,比如 `exploration`,像这样:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-01-changing-test-name/src/lib.rs}}
|
||||
@ -76,7 +76,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
让我们增加另一个测试,不过这一次是一个会失败的测试!当测试函数中出现 panic 时测试就失败了。每一个测试都在一个新线程中运行,当主线程发现测试线程异常了,就将对应测试标记为失败。第九章讲到了最简单的造成 panic 的方法:调用 `panic!` 宏。写入新测试 `another` 后, `src/lib.rs` 现在看起来如示例 11-3 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,panics,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-03/src/lib.rs:here}}
|
||||
@ -104,7 +104,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
回忆一下第五章中,示例 5-15 中有一个 `Rectangle` 结构体和一个 `can_hold` 方法,在示例 11-5 中再次使用他们。将他们放进 *src/lib.rs* 并使用 `assert!` 宏编写一些测试。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-05/src/lib.rs:here}}
|
||||
@ -114,7 +114,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
`can_hold` 方法返回一个布尔值,这意味着它完美符合 `assert!` 宏的使用场景。在示例 11-6 中,让我们编写一个 `can_hold` 方法的测试来作为练习,这里创建一个长为 8 宽为 7 的 `Rectangle` 实例,并假设它可以放得下另一个长为 5 宽为 1 的 `Rectangle` 实例:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-06/src/lib.rs:here}}
|
||||
@ -132,7 +132,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
它确实通过了!再来增加另一个测试,这一回断言一个更小的矩形不能放下一个更大的矩形:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/src/lib.rs:here}}
|
||||
@ -164,7 +164,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
示例 11-7 中,让我们编写一个对其参数加二并返回结果的函数 `add_two`。接着使用 `assert_eq!` 宏测试这个函数。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-07/src/lib.rs}}
|
||||
@ -206,7 +206,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
例如,比如说有一个根据人名进行问候的函数,而我们希望测试将传递给函数的人名显示在输出中:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-05-greeter/src/lib.rs}}
|
||||
@ -248,7 +248,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
示例 11-8 展示了一个检查 `Guess::new` 是否按照我们的期望出错的测试:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-08/src/lib.rs}}
|
||||
@ -278,7 +278,7 @@ Cargo 编译并运行了测试。在 `Compiling`、`Finished` 和 `Running` 这
|
||||
|
||||
然而 `should_panic` 测试结果可能会非常含糊不清,因为它只是告诉我们代码并没有产生 panic。`should_panic` 甚至在一些不是我们期望的原因而导致 panic 时也会通过。为了使 `should_panic` 测试结果更精确,我们可以给 `should_panic` 属性增加一个可选的 `expected` 参数。测试工具会确保错误信息中包含其提供的文本。例如,考虑示例 11-9 中修改过的 `Guess`,这里 `new` 函数根据其值是过大还或者过小而提供不同的 panic 信息:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-09/src/lib.rs:here}}
|
||||
|
@ -28,7 +28,7 @@ $ cargo test -- --test-threads=1
|
||||
|
||||
例如,示例 11-10 有一个无意义的函数,它打印出其参数的值并接着返回 10。接着还有一个会通过的测试和一个会失败的测试:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,panics,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-10/src/lib.rs}}
|
||||
@ -62,7 +62,7 @@ $ cargo test -- --show-output
|
||||
|
||||
为了展示如何运行部分测试,示例 11-11 为 `add_two` 函数创建了三个测试,我们可以选择具体运行哪一个:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-11/src/lib.rs}}
|
||||
@ -102,7 +102,7 @@ $ cargo test -- --show-output
|
||||
|
||||
有时一些特定的测试执行起来是非常耗费时间的,所以在大多数运行 `cargo test` 的时候希望能排除他们。虽然可以通过参数列举出所有希望运行的测试来做到,也可以使用 `ignore` 属性来标记耗时的测试并排除他们,如下所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-11-ignore-a-test/src/lib.rs}}
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
回忆本章第一部分新建的 `adder` 项目,Cargo 为我们生成了如下代码:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}}
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
测试社区中一直存在关于是否应该对私有函数直接进行测试的论战,而在其他语言中想要测试私有函数是一件困难的,甚至是不可能的事。不过无论你坚持哪种测试意识形态,Rust 的私有性规则确实允许你测试私有函数。考虑示例 11-12 中带有私有函数 `internal_adder` 的代码:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-12/src/lib.rs}}
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
让我们来创建一个集成测试。保留示例 11-12 中 *src/lib.rs* 的代码。创建一个 *tests* 目录,新建一个文件 *tests/integration_test.rs*,并输入示例 11-13 中的代码。
|
||||
|
||||
<span class="filename">文件名: tests/integration_test.rs</span>
|
||||
<span class="filename">文件名:tests/integration_test.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-13/tests/integration_test.rs}}
|
||||
@ -88,7 +88,7 @@
|
||||
|
||||
当你有一些在多个集成测试文件都会用到的帮助函数,而你尝试按照第七章 “将模块移动到其他文件” 部分的步骤将他们提取到一个通用的模块中时, *tests* 目录中不同文件的行为就会显得很明显。例如,如果我们可以创建 一个*tests/common.rs* 文件并创建一个名叫 `setup` 的函数,我们希望这个函数能被多个测试文件的测试函数调用:
|
||||
|
||||
<span class="filename">文件名: tests/common.rs</span>
|
||||
<span class="filename">文件名:tests/common.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/tests/common.rs}}
|
||||
@ -106,7 +106,7 @@
|
||||
|
||||
一旦拥有了 *tests/common/mod.rs*,就可以将其作为模块以便在任何集成测试文件中使用。这里是一个 *tests/integration_test.rs* 中调用 `setup` 函数的 `it_adds_two` 测试的例子:
|
||||
|
||||
<span class="filename">文件名: tests/integration_test.rs</span>
|
||||
<span class="filename">文件名:tests/integration_test.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-13-fix-shared-test-code-problem/tests/integration_test.rs}}
|
||||
|
@ -26,7 +26,7 @@ $ cargo run searchstring example-filename.txt
|
||||
|
||||
使用示例 12-1 中的代码来读取任何传递给 `minigrep` 的命令行参数并将其收集到一个 vector 中。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-01/src/main.rs}}
|
||||
@ -58,7 +58,7 @@ $ cargo run searchstring example-filename.txt
|
||||
|
||||
打印出参数 vector 中的值展示了程序可以访问指定为命令行参数的值。现在需要将这两个参数的值保存进变量这样就可以在程序的余下部分使用这些值了。让我们如示例 12-2 这样做:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic,noplayground
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-02/src/main.rs}}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
现在我们要增加读取由 `filename` 命令行参数指定的文件的功能。首先,需要一个用来测试的示例文件:用来确保 `minigrep` 正常工作的最好的文件是拥有多行少量文本且有一些重复单词的文件。示例 12-3 是一首艾米莉·狄金森(Emily Dickinson)的诗,它正适合这个工作!在项目根目录创建一个文件 `poem.txt`,并输入诗 "I'm nobody! Who are you?":
|
||||
|
||||
<span class="filename">文件名: poem.txt</span>
|
||||
<span class="filename">文件名:poem.txt</span>
|
||||
|
||||
```text
|
||||
{{#include ../listings/ch12-an-io-project/listing-12-03/poem.txt}}
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
创建完这个文件之后,修改 *src/main.rs* 并增加如示例 12-4 所示的打开文件的代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic,noplayground
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-04/src/main.rs:here}}
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
首先,我们将解析参数的功能提取到一个 `main` 将会调用的函数中,为将命令行解析逻辑移动到 *src/lib.rs* 中做准备。示例 12-5 中展示了新 `main` 函数的开头,它调用了新函数 `parse_config`。目前它仍将定义在 *src/main.rs* 中:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-05/src/main.rs:here}}
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
示例 12-6 展示了 `parse_config` 函数的改进。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic,noplayground
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-06/src/main.rs:here}}
|
||||
@ -86,7 +86,7 @@
|
||||
|
||||
所以现在 `parse_config` 函数的目的是创建一个 `Config` 实例,我们可以将 `parse_config` 从一个普通函数变为一个叫做 `new` 的与结构体关联的函数。做出这个改变使得代码更符合习惯:可以像标准库中的 `String` 调用 `String::new` 来创建一个该类型的实例那样,将 `parse_config` 变为一个与 `Config` 关联的 `new` 函数。示例 12-7 展示了需要做出的修改:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,should_panic,noplayground
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-07/src/main.rs:here}}
|
||||
@ -110,7 +110,7 @@
|
||||
|
||||
在示例 12-8 中,在 `new` 函数中增加了一个检查在访问索引 `1` 和 `2` 之前检查 slice 是否足够长。如果 slice 不够长,我们使用一个更好的错误信息 panic 而不是 `index out of bounds` 信息:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-08/src/main.rs:here}}
|
||||
@ -134,7 +134,7 @@
|
||||
|
||||
示例 12-9 展示了为了返回 `Result` 在 `Config::new` 的返回值和函数体中所需的改变。注意这还不能编译,直到下一个示例同时也更新了 `main` 之后。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-09/src/main.rs:here}}
|
||||
@ -152,7 +152,7 @@
|
||||
|
||||
为了处理错误情况并打印一个对用户友好的信息,我们需要像示例 12-10 那样更新 `main` 函数来处理现在 `Config::new` 返回的 `Result`。另外还需要手动实现原先由 `panic!`负责的工作,即以非零错误码退出命令行工具的工作。非零的退出状态是一个惯例信号,用来告诉调用程序的进程:该程序以错误状态退出了。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-10/src/main.rs:here}}
|
||||
@ -176,7 +176,7 @@
|
||||
|
||||
示例 12-11 展示了提取出来的 `run` 函数。目前我们只进行小的增量式的提取函数的改进。我们仍将在 *src/main.rs* 中定义这个函数:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-11/src/main.rs:here}}
|
||||
@ -190,7 +190,7 @@
|
||||
|
||||
通过将剩余的逻辑分离进 `run` 函数而不是留在 `main` 中,就可以像示例 12-9 中的 `Config::new` 那样改进错误处理。不再通过 `expect` 允许程序 panic,`run` 函数将会在出错时返回一个 `Result<T, E>`。这让我们进一步以一种对用户友好的方式统一 `main` 中的错误处理。示例 12-12 展示了 `run` 签名和函数体中的改变:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-12/src/main.rs:here}}
|
||||
@ -218,7 +218,7 @@ Rust 提示我们的代码忽略了 `Result` 值,它可能表明这里存在
|
||||
|
||||
我们将检查错误并使用类似示例 12-10 中 `Config::new` 处理错误的技术来处理他们,不过有一些细微的不同:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/no-listing-01-handling-errors-in-main/src/main.rs:here}}
|
||||
@ -241,7 +241,7 @@ Rust 提示我们的代码忽略了 `Result` 值,它可能表明这里存在
|
||||
|
||||
现在 *src/lib.rs* 的内容应该看起来像示例 12-13(为了简洁省略了函数体)。注意直到下一个示例修改完 *src/main.rs* 之后,代码还不能编译:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-13/src/lib.rs:here}}
|
||||
@ -253,7 +253,7 @@ Rust 提示我们的代码忽略了 `Result` 值,它可能表明这里存在
|
||||
|
||||
现在需要在 *src/main.rs* 中将移动到 *src/lib.rs* 的代码引入二进制 crate 的作用域中,如示例 12-14 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-14/src/main.rs:here}}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
去掉 *src/lib.rs* 和 *src/main.rs* 中用于检查程序行为的 `println!` 语句,因为不再真正需要他们了。接着我们会像 [第十一章][ch11-anatomy] 那样增加一个 `test` 模块和一个测试函数。测试函数指定了 `search` 函数期望拥有的行为:它会获取一个需要查询的字符串和用来查询的文本,并只会返回包含请求的文本行。示例 12-15 展示了这个测试,它还不能编译:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-15/src/lib.rs:here}}
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
我们还不能运行这个测试并看到它失败,因为它甚至都还不能编译:`search` 函数还不存在呢!我们将增加足够的代码来使其能够编译:一个总是会返回空 vector 的 `search` 函数定义,如示例 12-16 所示。然后这个测试应该能够编译并因为空 vector 并不匹配一个包含一行 `"safe, fast, productive."` 的 vector 而失败。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-16/src/lib.rs:here}}
|
||||
@ -79,7 +79,7 @@ Rust 不可能知道我们需要的是哪一个参数,所以需要告诉它。
|
||||
|
||||
Rust 有一个有助于一行一行遍历字符串的方法,出于方便它被命名为 `lines`,它如示例 12-17 这样工作。注意这还不能编译:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-17/src/lib.rs:here}}
|
||||
@ -93,7 +93,7 @@ Rust 有一个有助于一行一行遍历字符串的方法,出于方便它被
|
||||
|
||||
接下来将会增加检查当前行是否包含查询字符串的功能。幸运的是,字符串类型为此也有一个叫做 `contains` 的实用方法!如示例 12-18 所示在 `search` 函数中加入 `contains` 方法调用。注意这仍然不能编译:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-18/src/lib.rs:here}}
|
||||
@ -105,7 +105,7 @@ Rust 有一个有助于一行一行遍历字符串的方法,出于方便它被
|
||||
|
||||
我们还需要一个方法来存储包含查询字符串的行。为此可以在 `for` 循环之前创建一个可变的 vector 并调用 `push` 方法在 vector 中存放一个 `line`。在 `for` 循环之后,返回这个 vector,如示例 12-19 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-19/src/lib.rs:here}}
|
||||
@ -127,7 +127,7 @@ Rust 有一个有助于一行一行遍历字符串的方法,出于方便它被
|
||||
|
||||
现在 `search` 函数是可以工作并测试通过了的,我们需要实际在 `run` 函数中调用 `search`。需要将 `config.query` 值和 `run` 从文件中读取的 `contents` 传递给 `search` 函数。接着 `run` 会打印出 `search` 返回的每一行:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/no-listing-02-using-search-in-run/src/lib.rs:here}}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
我们希望增加一个新函数 `search_case_insensitive`,并将会在设置了环境变量时调用它。这里将继续遵循 TDD 过程,其第一步是再次编写一个失败测试。我们将为新的大小写不敏感搜索函数新增一个测试函数,并将老的测试函数从 `one_result` 改名为 `case_sensitive` 来更清楚的表明这两个测试的区别,如示例 12-20 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-20/src/lib.rs:here}}
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
`search_case_insensitive` 函数,如示例 12-21 所示,将与 `search` 函数基本相同。唯一的区别是它会将 `query` 变量和每一 `line` 都变为小写,这样不管输入参数是大写还是小写,在检查该行是否包含查询字符串时都会是小写。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-21/src/lib.rs:here}}
|
||||
@ -48,7 +48,7 @@
|
||||
|
||||
好的!现在,让我们在 `run` 函数中实际调用新 `search_case_insensitive` 函数。首先,我们将在 `Config` 结构体中增加一个配置项来切换大小写敏感和大小写不敏感搜索。增加这些字段会导致编译错误,因为我们还没有在任何地方初始化这些字段:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-22/src/lib.rs:here}}
|
||||
@ -56,7 +56,7 @@
|
||||
|
||||
这里增加了 `case_sensitive` 字符来存放一个布尔值。接着我们需要 `run` 函数检查 `case_sensitive` 字段的值并使用它来决定是否调用 `search` 函数或 `search_case_insensitive` 函数,如示例 12-22 所示。注意这还不能编译:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-22/src/lib.rs:there}}
|
||||
@ -66,7 +66,7 @@
|
||||
|
||||
最后需要实际检查环境变量。处理环境变量的函数位于标准库的 `env` 模块中,所以我们需要在 *src/lib.rs* 的开头增加一个 `use std::env;` 行将这个模块引入作用域中。接着在 `Config::new` 中使用 `env` 模块的 `var` 方法来检查一个叫做 `CASE_INSENSITIVE` 的环境变量,如示例 12-23 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-23/src/lib.rs:here}}
|
||||
|
@ -32,7 +32,7 @@ Problem parsing arguments: not enough arguments
|
||||
|
||||
让我们如示例 12-24 所示的代码改变错误信息是如何被打印的。得益于本章早些时候的重构,所有打印错误信息的代码都位于 `main` 一个函数中。标准库提供了 `eprintln!` 宏来打印到标准错误流,所以将两个调用 `println!` 打印错误信息的位置替换为 `eprintln!`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-24/src/main.rs:here}}
|
||||
@ -57,7 +57,7 @@ $ cargo run to poem.txt > output.txt
|
||||
|
||||
我们并不会在终端看到任何输出,同时 `output.txt` 将会包含其结果:
|
||||
|
||||
<span class="filename">文件名: output.txt</span>
|
||||
<span class="filename">文件名:output.txt</span>
|
||||
|
||||
```text
|
||||
Are you nobody, too?
|
||||
|
@ -14,7 +14,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
这里将通过调用 `simulated_expensive_calculation` 函数来模拟调用假定的算法,如示例 13-1 所示,它会打印出 `calculating slowly...`,等待两秒,并接着返回传递给它的数字:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-01/src/main.rs:here}}
|
||||
@ -31,7 +31,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
程序的输出将会是建议的锻炼计划。示例 13-2 展示了我们将要使用的 `main` 函数:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-02/src/main.rs:here}}
|
||||
@ -43,7 +43,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
现在有了执行上下文,让我们编写算法。示例 13-3 中的 `generate_workout` 函数包含本例中我们最关心的 app 业务逻辑。本例中余下的代码修改都将在这个函数中进行:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-03/src/main.rs:here}}
|
||||
@ -65,7 +65,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
有多种方法可以重构此程序。我们首先尝试的是将重复的 `simulated_expensive_calculation` 函数调用提取到一个变量中,如示例 13-4 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-04/src/main.rs:here}}
|
||||
@ -81,7 +81,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
不同于总是在 `if` 块之前调用 `simulated_expensive_calculation` 函数并储存其结果,我们可以定义一个闭包并将其储存在变量中,如示例 13-5 所示。实际上可以选择将整个 `simulated_expensive_calculation` 函数体移动到这里引入的闭包中:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-05/src/main.rs:here}}
|
||||
@ -97,7 +97,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
定义了闭包之后,可以改变 `if` 块中的代码来调用闭包以执行代码并获取结果值。调用闭包类似于调用函数;指定存放闭包定义的变量名并后跟包含期望使用的参数的括号,如示例 13-6 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-06/src/main.rs:here}}
|
||||
@ -119,7 +119,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
类似于变量,如果相比严格的必要性你更希望增加明确性并变得更啰嗦,可以选择增加类型注解;为示例 13-5 中定义的闭包标注类型将看起来像示例 13-7 中的定义:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-07/src/main.rs:here}}
|
||||
@ -140,7 +140,7 @@ let add_one_v4 = |x| x + 1 ;
|
||||
|
||||
闭包定义会为每个参数和返回值推断一个具体类型。例如,示例 13-8 中展示了仅仅将参数作为返回值的简短的闭包定义。除了作为示例的目的这个闭包并不是很实用。注意其定义并没有增加任何类型注解:如果尝试调用闭包两次,第一次使用 `String` 类型作为参数而第二次使用 `u32`,则会得到一个错误:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-08/src/main.rs:here}}
|
||||
@ -170,7 +170,7 @@ let add_one_v4 = |x| x + 1 ;
|
||||
|
||||
示例 13-9 展示了存放了闭包和一个 Option 结果值的 `Cacher` 结构体的定义:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-09/src/main.rs:here}}
|
||||
@ -186,7 +186,7 @@ let add_one_v4 = |x| x + 1 ;
|
||||
|
||||
刚才讨论的有关 `value` 字段逻辑定义于示例 13-10:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-10/src/main.rs:here}}
|
||||
@ -204,7 +204,7 @@ let add_one_v4 = |x| x + 1 ;
|
||||
|
||||
示例 13-11 展示了如何在示例 13-6 的 `generate_workout` 函数中利用 `Cacher` 结构体:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-11/src/main.rs:here}}
|
||||
@ -246,7 +246,7 @@ let add_one_v4 = |x| x + 1 ;
|
||||
|
||||
示例 13-12 有一个储存在 `equal_to_x` 变量中闭包的例子,它使用了闭包环境中的变量 `x`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-12/src/main.rs}}
|
||||
@ -258,7 +258,7 @@ let add_one_v4 = |x| x + 1 ;
|
||||
|
||||
函数则不能做到同样的事,如果尝试如下例子,它并不能编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/no-listing-02-functions-cant-capture/src/main.rs}}
|
||||
@ -288,7 +288,7 @@ let add_one_v4 = |x| x + 1 ;
|
||||
|
||||
第十六章讨论并发时会展示更多 `move` 闭包的例子,不过现在这里修改了示例 13-12 中的代码(作为演示),在闭包定义中增加 `move` 关键字并使用 vector 代替整型,因为整型可以被拷贝而不是移动;注意这些代码还不能编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/no-listing-03-move-closures/src/main.rs}}
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
在标准库中没有提供迭代器的语言中,我们可能会使用一个从 0 开始的索引变量,使用这个变量索引 vector 中的值,并循环增加其值直到达到 vector 的元素数量。
|
||||
|
||||
迭代器为我们处理了所有这些逻辑,这减少了重复代码并消除了潜在的混乱。另外,迭代器的实现方式提供了对多种不同的序列使用相同逻辑的灵活性,而不仅仅是像 vector 这样可索引的数据结构.让我们看看迭代器是如何做到这些的。
|
||||
迭代器为我们处理了所有这些逻辑,这减少了重复代码并消除了潜在的混乱。另外,迭代器的实现方式提供了对多种不同的序列使用相同逻辑的灵活性,而不仅仅是像 vector 这样可索引的数据结构。让我们看看迭代器是如何做到这些的。
|
||||
|
||||
### `Iterator` trait 和 `next` 方法
|
||||
|
||||
@ -48,7 +48,7 @@ pub trait Iterator {
|
||||
|
||||
可以直接调用迭代器的 `next` 方法;示例 13-15 有一个测试展示了重复调用由 vector 创建的迭代器的 `next` 方法所得到的值:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-15/src/lib.rs:here}}
|
||||
@ -67,7 +67,7 @@ pub trait Iterator {
|
||||
|
||||
这些调用 `next` 方法的方法被称为 **消费适配器**(*consuming adaptors*),因为调用他们会消耗迭代器。一个消费适配器的例子是 `sum` 方法。这个方法获取迭代器的所有权并反复调用 `next` 来遍历迭代器,因而会消费迭代器。当其遍历每一个项时,它将每一个项加总到一个总和并在迭代完成时返回总和。示例 13-16 有一个展示 `sum` 方法使用的测试:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-16/src/lib.rs:here}}
|
||||
@ -83,7 +83,7 @@ pub trait Iterator {
|
||||
|
||||
示例 13-17 展示了一个调用迭代器适配器方法 `map` 的例子,该 `map` 方法使用闭包来调用每个元素以生成新的迭代器。这里的闭包创建了一个新的迭代器,对其中 vector 中的每个元素都被加 1。不过这些代码会产生一个警告:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,not_desired_behavior
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-17/src/main.rs:here}}
|
||||
@ -103,7 +103,7 @@ pub trait Iterator {
|
||||
|
||||
在示例 13-18 中,我们将遍历由 `map` 调用生成的迭代器的结果收集到一个 vector 中,它将会含有原始 vector 中每个元素加 1 的结果:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-18/src/main.rs:here}}
|
||||
@ -119,7 +119,7 @@ pub trait Iterator {
|
||||
|
||||
示例 13-19 展示了使用 `filter` 和一个捕获环境中变量 `shoe_size` 的闭包,这样闭包就可以遍历一个 `Shoe` 结构体集合以便只返回指定大小的鞋子:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-19/src/lib.rs}}
|
||||
@ -143,7 +143,7 @@ pub trait Iterator {
|
||||
|
||||
示例 13-20 有一个 `Counter` 结构体定义和一个创建 `Counter` 实例的关联函数 `new`:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-20/src/lib.rs}}
|
||||
@ -155,7 +155,7 @@ pub trait Iterator {
|
||||
|
||||
接下来将为 `Counter` 类型实现 `Iterator` trait,通过定义 `next` 方法来指定使用迭代器时的行为,如示例 13-21 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-21/src/lib.rs:here}}
|
||||
@ -171,7 +171,7 @@ pub trait Iterator {
|
||||
|
||||
一旦实现了 `Iterator` trait,我们就有了一个迭代器!示例 13-22 展示了一个测试用来演示使用 `Counter` 结构体的迭代器功能,通过直接调用 `next` 方法,正如示例 13-15 中从 vector 创建的迭代器那样:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-22/src/lib.rs:here}}
|
||||
@ -187,7 +187,7 @@ pub trait Iterator {
|
||||
|
||||
例如,出于某种原因我们希望获取 `Counter` 实例产生的值,将这些值与另一个 `Counter` 实例在省略了第一个值之后产生的值配对,将每一对值相乘,只保留那些可以被三整除的结果,然后将所有保留的结果相加,这可以如示例 13-23 中的测试这样做:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-23/src/lib.rs:here}}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
在示例 12-6 中,我们增加了一些代码获取一个 `String` slice 并创建一个 `Config` 结构体的实例,他们索引 slice 中的值并克隆这些值以便 `Config` 结构体可以拥有这些值。在示例 13-24 中重现了第十二章结尾示例 12-23 中 `Config::new` 函数的实现:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-12-23-reproduced/src/lib.rs:ch13}}
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
打开 I/O 项目的 *src/main.rs* 文件,它看起来应该像这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-12-24-reproduced/src/main.rs:ch13}}
|
||||
@ -38,7 +38,7 @@
|
||||
|
||||
修改第十二章结尾示例 12-24 中的 `main` 函数的开头为示例 13-25 中的代码。在更新 `Config::new` 之前这些代码还不能编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-25/src/main.rs:here}}
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
接下来需要更新 `Config::new` 的定义。在 I/O 项目的 *src/lib.rs* 中,将 `Config::new` 的签名改为如示例 13-26 所示。这仍然不能编译因为我们还需更新函数体:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-26/src/lib.rs:here}}
|
||||
@ -64,7 +64,7 @@
|
||||
|
||||
接下来,我们将修改 `Config::new` 的内容。标准库文档还提到 `std::env::Args` 实现了 `Iterator` trait,因此我们知道可以对其调用 `next` 方法!示例 13-27 更新了示例 12-23 中的代码,以使用 `next` 方法:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-27/src/lib.rs:here}}
|
||||
@ -78,7 +78,7 @@
|
||||
|
||||
I/O 项目中其他可以利用迭代器的地方是 `search` 函数,示例 13-28 中重现了第十二章结尾示例 12-19 中此函数的定义:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch12-an-io-project/listing-12-19/src/lib.rs:ch13}}
|
||||
@ -88,7 +88,7 @@ I/O 项目中其他可以利用迭代器的地方是 `search` 函数,示例 13
|
||||
|
||||
可以通过使用迭代器适配器方法来编写更简明的代码。这也避免了一个可变的中间 `results` vector 的使用。函数式编程风格倾向于最小化可变状态的数量来使代码更简洁。去掉可变状态可能会使得将来进行并行搜索的增强变得更容易,因为我们不必管理 `results` vector 的并发访问。示例 13-29 展示了该变化:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-29/src/lib.rs:here}}
|
||||
|
@ -17,7 +17,7 @@ test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
|
||||
|
||||
对于一个更全面的性能测试,将会检查不同长度的文本、不同的搜索单词、不同长度的单词和所有其他的可变情况。这里所要表达的是:迭代器,作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能代码。迭代器是 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.
|
||||
> 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.
|
||||
>
|
||||
> - Bjarne Stroustrup "Foundations of C++"
|
||||
>
|
||||
|
@ -21,7 +21,7 @@ $ cargo build --release
|
||||
|
||||
当项目的 *Cargo.toml* 文件中没有任何 `[profile.*]` 部分的时候,Cargo 会对每一个配置都采用默认设置。通过增加任何希望定制的配置对应的 `[profile.*]` 部分,我们可以选择覆盖任意默认设置的子集。例如,如下是 `dev` 和 `release` 配置的 `opt-level` 设置的默认值:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[profile.dev]
|
||||
@ -35,7 +35,7 @@ opt-level = 3
|
||||
|
||||
我们可以选择通过在 *Cargo.toml* 增加不同的值来覆盖任何默认设置。比如,如果我们想要在开发配置中使用级别 1 的优化,则可以在 *Cargo.toml* 中增加这两行:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[profile.dev]
|
||||
|
@ -13,7 +13,7 @@ Rust 和 Cargo 有一些帮助他人更方便找到和使用你发布的包的
|
||||
|
||||
文档注释使用三斜杠 `///` 而不是两斜杆以支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。示例 14-1 展示了一个 `my_crate` crate 中 `add_one` 函数的文档注释,
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-01/src/lib.rs}}
|
||||
@ -60,7 +60,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
作为一个例子,如果我们希望增加描述包含 `add_one` 函数的 `my_crate` crate 目的的文档,可以在 _src/lib.rs_ 开头增加以 `//!` 开头的注释,如示例 14-2 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-02/src/lib.rs:here}}
|
||||
@ -88,7 +88,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
例如,假设我们创建了一个描述美术信息的库 `art`。这个库中包含了一个有两个枚举 `PrimaryColor` 和 `SecondaryColor` 的模块 `kinds`,以及一个包含函数 `mix` 的模块 `utils`,如示例 14-3 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground,test_harness
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-03/src/lib.rs:here}}
|
||||
@ -106,7 +106,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
另一个依赖这个库的 crate 需要 `use` 语句来导入 `art` 中的项,这包含指定其当前定义的模块结构。示例 14-4 展示了一个使用 `art` crate 中 `PrimaryColor` 和 `mix` 项的 crate 的例子:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-04/src/main.rs}}
|
||||
@ -118,7 +118,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
为了从公有 API 中去掉 crate 的内部组织,我们可以采用示例 14-3 中的 `art` crate 并增加 `pub use` 语句来重导出项到顶层结构,如示例 14-5 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-05/src/lib.rs:here}}
|
||||
@ -134,7 +134,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
`art` crate 的用户仍然可以看见和选择使用示例 14-4 中的内部结构,或者可以使用示例 14-5 中更为方便的结构,如示例 14-6 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-06/src/main.rs:here}}
|
||||
@ -162,7 +162,7 @@ $ cargo login abcdefghijklmnopqrstuvwxyz012345
|
||||
|
||||
首先 crate 需要一个唯一的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名称。不过 [crates.io](https://crates.io)<!-- ignore --> 上的 crate 名称遵守先到先得的分配原则。一旦某个 crate 名称被使用,其他人就不能再发布这个名称的 crate 了。请在网站上搜索你希望使用的名称来找出它是否已被使用。如果没有,修改 _Cargo.toml_ 中 `[package]` 里的名称为你希望用于发布的名称,像这样:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[package]
|
||||
@ -189,7 +189,7 @@ Caused by:
|
||||
|
||||
[spdx]: http://spdx.org/licenses/
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[package]
|
||||
@ -203,7 +203,7 @@ license = "MIT"
|
||||
|
||||
那么,有了唯一的名称、版本号、由 `cargo new` 新建项目时增加的作者信息、描述和所选择的 license,已经准备好发布的项目的 _Cargo.toml_ 文件可能看起来像这样:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
[package]
|
||||
|
@ -17,7 +17,7 @@ $ cd add
|
||||
|
||||
接着在 *add* 目录中,创建 *Cargo.toml* 文件。这个 *Cargo.toml* 文件配置了整个工作空间。它不会包含 `[package]` 或其他我们在 *Cargo.toml* 中见过的元信息。相反,它以 `[workspace]` 部分作为开始,并通过指定 *adder* 的路径来为工作空间增加成员,如下会加入二进制 crate:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch14-more-about-cargo/no-listing-01-workspace-with-adder-crate/add/Cargo.toml}}
|
||||
@ -48,7 +48,7 @@ $ cargo new adder
|
||||
|
||||
接下来,让我们在工作空间中指定另一个成员 crate。这个 crate 位于 *add_one* 目录中,所以修改顶级 *Cargo.toml* 为也包含 *add_one* 路径:
|
||||
|
||||
<span class="filename">文件名: Cargo.toml</span>
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.toml}}
|
||||
@ -79,7 +79,7 @@ $ cargo new add_one --lib
|
||||
|
||||
在 *add_one/src/lib.rs* 文件中,增加一个 `add_one` 函数:
|
||||
|
||||
<span class="filename">文件名: add_one/src/lib.rs</span>
|
||||
<span class="filename">文件名:add_one/src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add_one/src/lib.rs}}
|
||||
@ -87,7 +87,7 @@ $ cargo new add_one --lib
|
||||
|
||||
现在工作空间中有了一个库 crate,让 `adder` 依赖库 crate `add_one`。首先需要在 *adder/Cargo.toml* 文件中增加 `add_one` 作为路径依赖:
|
||||
|
||||
<span class="filename">文件名: adder/Cargo.toml</span>
|
||||
<span class="filename">文件名:adder/Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/adder/Cargo.toml:6:7}}
|
||||
@ -97,7 +97,7 @@ cargo并不假定工作空间中的Crates会相互依赖,所以需要明确表
|
||||
|
||||
接下来,在 `adder` crate 中使用 `add_one` crate 的函数 `add_one`。打开 *adder/src/main.rs* 在顶部增加一行 `use` 将新 `add_one` 库 crate 引入作用域。接着修改 `main` 函数来调用 `add_one` 函数,如示例 14-7 所示。
|
||||
|
||||
<span class="filename">文件名: adder/src/main.rs</span>
|
||||
<span class="filename">文件名:adder/src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-07/add/adder/src/main.rs}}
|
||||
@ -130,7 +130,7 @@ Hello, world! 10 plus one is 11!
|
||||
|
||||
还需注意的是工作空间只在根目录有一个 *Cargo.lock*,而不是在每一个 crate 目录都有 *Cargo.lock*。这确保了所有的 crate 都使用完全相同版本的依赖。如果在 *Cargo.toml* 和 *add_one/Cargo.toml* 中都增加 `rand` crate,则 Cargo 会将其都解析为同一版本并记录到唯一的 *Cargo.lock* 中。使得工作空间中的所有 crate 都使用相同的依赖意味着其中的 crate 都是相互兼容的。让我们在 *add_one/Cargo.toml* 中的 `[dependencies]` 部分增加 `rand` crate 以便能够在 `add_one` crate 中使用 `rand` crate:
|
||||
|
||||
<span class="filename">文件名: add_one/Cargo.toml</span>
|
||||
<span class="filename">文件名:add_one/Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add_one/Cargo.toml:6:7}}
|
||||
@ -178,7 +178,7 @@ error[E0432]: unresolved import `rand`
|
||||
|
||||
作为另一个提升,让我们为 `add_one` crate 中的 `add_one::add_one` 函数增加一个测试:
|
||||
|
||||
<span class="filename">文件名: add_one/src/lib.rs</span>
|
||||
<span class="filename">文件名:add_one/src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/src/lib.rs}}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
示例 15-1 展示了如何使用 box 在堆上储存一个 `i32`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-01/src/main.rs}}
|
||||
@ -49,7 +49,7 @@ cons list 的每一项都包含两个元素:当前项的值和下一项。其
|
||||
|
||||
示例 15-2 包含一个 cons list 的枚举定义。注意这还不能编译因为这个类型没有已知的大小,之后我们会展示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-02/src/main.rs:here}}
|
||||
@ -61,7 +61,7 @@ cons list 的每一项都包含两个元素:当前项的值和下一项。其
|
||||
|
||||
使用这个 cons list 来储存列表 `1, 2, 3` 将看起来如示例 15-3 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-03/src/main.rs:here}}
|
||||
@ -114,7 +114,7 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` repre
|
||||
|
||||
我们可以修改示例 15-2 中 `List` 枚举的定义和示例 15-3 中对 `List` 的应用,如示例 15-65 所示,这是可以编译的:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-05/src/main.rs}}
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
常规引用是一个指针类型,一种理解指针的方式是将其看成指向储存在其他某处值的箭头。在示例 15-6 中,创建了一个 `i32` 值的引用,接着使用解引用运算符来跟踪所引用的数据:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-06/src/main.rs}}
|
||||
@ -35,7 +35,7 @@
|
||||
|
||||
可以使用 `Box<T>` 代替引用来重写示例 15-6 中的代码,解引用运算符也一样能工作,如示例 15-7 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-07/src/main.rs}}
|
||||
@ -51,7 +51,7 @@
|
||||
|
||||
从根本上说,`Box<T>` 被定义为包含一个元素的元组结构体,所以示例 15-8 以相同的方式定义了 `MyBox<T>` 类型。我们还定义了 `new` 函数来对应定义于 `Box<T>` 的 `new` 函数:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-08/src/main.rs:here}}
|
||||
@ -63,7 +63,7 @@
|
||||
|
||||
尝试将示例 15-7 中的代码加入示例 15-8 中并修改 `main` 使用我们定义的 `MyBox<T>` 类型代替 `Box<T>`。示例 15-9 中的代码不能编译,因为 Rust 不知道如何解引用 `MyBox`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-09/src/main.rs:here}}
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
如第十章 [“为类型实现 trait”][impl-trait] 部分所讨论的,为了实现 trait,需要提供 trait 所需的方法实现。`Deref` trait,由标准库提供,要求实现名为 `deref` 的方法,其借用 `self` 并返回一个内部数据的引用。示例 15-10 包含定义于 `MyBox` 之上的 `Deref` 实现:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-10/src/main.rs:here}}
|
||||
@ -117,7 +117,7 @@ Deref 强制转换的加入使得 Rust 程序员编写函数和方法调用时
|
||||
|
||||
作为展示 Deref 强制转换的实例,让我们使用示例 15-8 中定义的 `MyBox<T>`,以及示例 15-10 中增加的 `Deref` 实现。示例 15-11 展示了一个有着字符串 slice 参数的函数定义:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-11/src/main.rs:here}}
|
||||
@ -127,7 +127,7 @@ Deref 强制转换的加入使得 Rust 程序员编写函数和方法调用时
|
||||
|
||||
可以使用字符串 slice 作为参数调用 `hello` 函数,比如 `hello("Rust");`。Deref 强制转换使得用 `MyBox<String>` 类型值的引用调用 `hello` 成为可能,如示例 15-12 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-12/src/main.rs:here}}
|
||||
@ -139,7 +139,7 @@ Deref 强制转换的加入使得 Rust 程序员编写函数和方法调用时
|
||||
|
||||
如果 Rust 没有实现 Deref 强制转换,为了使用 `&MyBox<String>` 类型的值调用 `hello`,则不得不编写示例 15-13 中的代码来代替示例 15-12:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-13/src/main.rs:here}}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
示例 15-14 展示了唯一定制功能就是当其实例离开作用域时,打印出 `Dropping CustomSmartPointer!` 的结构体 `CustomSmartPointer`。这会演示 Rust 何时运行 `drop` 函数:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-14/src/main.rs}}
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
如果我们像是示例 15-14 那样尝试调用 `Drop` trait 的 `drop` 方法,就会得到像示例 15-15 那样的编译错误:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-15/src/main.rs:here}}
|
||||
@ -59,7 +59,7 @@ Rust 不允许我们显式调用 `drop` 因为 Rust 仍然会在 `main` 的结
|
||||
|
||||
`std::mem::drop` 函数不同于 `Drop` trait 中的 `drop` 方法。可以通过传递希望提早强制丢弃的值作为参数。`std::mem::drop` 位于 prelude,所以我们可以修改示例 15-15 中的 `main` 来调用 `drop` 函数。如示例 15-16 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-16/src/main.rs:here}}
|
||||
|
@ -19,13 +19,13 @@
|
||||
|
||||
<img alt="Two lists that share ownership of a third list" src="img/trpl15-03.svg" class="center" />
|
||||
|
||||
<span class="caption">图 15-3: 两个列表, `b` 和 `c`, 共享第三个列表 `a` 的所有权</span>
|
||||
<span class="caption">图 15-3: 两个列表,`b` 和 `c`, 共享第三个列表 `a` 的所有权</span>
|
||||
|
||||
列表 `a` 包含 5 之后是 10,之后是另两个列表:`b` 从 3 开始而 `c` 从 4 开始。`b` 和 `c` 会接上包含 5 和 10 的列表 `a`。换句话说,这两个列表会尝试共享第一个列表所包含的 5 和 10。
|
||||
|
||||
尝试使用 `Box<T>` 定义的 `List` 实现并不能工作,如示例 15-17 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-17/src/main.rs}}
|
||||
@ -45,7 +45,7 @@
|
||||
|
||||
相反,我们修改 `List` 的定义为使用 `Rc<T>` 代替 `Box<T>`,如列表 15-18 所示。现在每一个 `Cons` 变量都包含一个值和一个指向 `List` 的 `Rc<T>`。当创建 `b` 时,不同于获取 `a` 的所有权,这里会克隆 `a` 所包含的 `Rc<List>`,这会将引用计数从 1 增加到 2 并允许 `a` 和 `b` 共享 `Rc<List>` 中数据的所有权。创建 `c` 时也会克隆 `a`,这会将引用计数从 2 增加为 3。每次调用 `Rc::clone`,`Rc<List>` 中数据的引用计数都会增加,直到有零个引用之前其数据都不会被清理。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-18/src/main.rs}}
|
||||
@ -63,7 +63,7 @@
|
||||
|
||||
在示例 15-19 中,修改了 `main` 以便将列表 `c` 置于内部作用域中,这样就可以观察当 `c` 离开作用域时引用计数如何变化。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-19/src/main.rs:here}}
|
||||
|
@ -60,7 +60,7 @@
|
||||
|
||||
该库只提供记录与最大值的差距,以及何种情况发送什么消息的功能。使用此库的程序则期望提供实际发送消息的机制:程序可以选择记录一条消息、发送 email、发送短信等等。库本身无需知道这些细节;只需实现其提供的 `Messenger` trait 即可。示例 15-20 展示了库代码:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-20/src/lib.rs}}
|
||||
@ -72,7 +72,7 @@
|
||||
|
||||
我们所需的 mock 对象是,调用 `send` 并不实际发送 email 或消息,而是只记录信息被通知要发送了。可以新建一个 mock 对象实例,用其创建 `LimitTracker`,调用 `LimitTracker` 的 `set_value` 方法,然后检查 mock 对象是否有我们期望的消息。示例 15-21 展示了一个如此尝试的 mock 对象实现,不过借用检查器并不允许:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-21/src/lib.rs:here}}
|
||||
@ -94,7 +94,7 @@
|
||||
|
||||
这正是内部可变性的用武之地!我们将通过 `RefCell` 来储存 `sent_messages`,然后 `send` 将能够修改 `sent_messages` 并储存消息。示例 15-22 展示了代码:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-22/src/lib.rs:here}}
|
||||
@ -118,7 +118,7 @@
|
||||
|
||||
如果我们尝试违反这些规则,相比引用时的编译时错误,`RefCell<T>` 的实现会在运行时出现 panic。示例 15-23 展示了对示例 15-22 中 `send` 实现的修改,这里我们故意尝试在相同作用域创建两个可变借用以便演示 `RefCell<T>` 不允许我们在运行时这么做:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,panics
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-23/src/lib.rs:here}}
|
||||
@ -142,7 +142,7 @@
|
||||
|
||||
例如,回忆示例 15-18 的 cons list 的例子中使用 `Rc<T>` 使得多个列表共享另一个列表的所有权。因为 `Rc<T>` 只存放不可变值,所以一旦创建了这些列表值后就不能修改。让我们加入 `RefCell<T>` 来获得修改列表中值的能力。示例 15-24 展示了通过在 `Cons` 定义中使用 `RefCell<T>`,我们就允许修改所有列表中的值了:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-24/src/main.rs}}
|
||||
|
@ -9,7 +9,7 @@ Rust 的内存安全性保证使其难以意外地制造永远也不会被清理
|
||||
|
||||
让我们看看引用循环是如何发生的以及如何避免它。以示例 15-25 中的 `List` 枚举和 `tail` 方法的定义开始:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-25/src/main.rs}}
|
||||
@ -21,7 +21,7 @@ Rust 的内存安全性保证使其难以意外地制造永远也不会被清理
|
||||
|
||||
在示例 15-26 中增加了一个 `main` 函数,其使用了示例 15-25 中的定义。这些代码在 `a` 中创建了一个列表,一个指向 `a` 中列表的 `b` 列表,接着修改 `a` 中的列表指向 `b` 中的列表,这会创建一个引用循环。在这个过程的多个位置有 `println!` 语句展示引用计数。
|
||||
|
||||
<span class="filename">文件: src/main.rs</span>
|
||||
<span class="filename">文件:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-26/src/main.rs:here}}
|
||||
@ -67,7 +67,7 @@ Rust 的内存安全性保证使其难以意外地制造永远也不会被清理
|
||||
|
||||
在最开始,我们将会构建一个带有子节点的树。让我们创建一个用于存放其拥有所有权的 `i32` 值和其子节点引用的 `Node`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-27/src/main.rs:here}}
|
||||
@ -77,7 +77,7 @@ Rust 的内存安全性保证使其难以意外地制造永远也不会被清理
|
||||
|
||||
接下来,使用此结构体定义来创建一个叫做 `leaf` 的带有值 3 且没有子节点的 `Node` 实例,和另一个带有值 5 并以 `leaf` 作为子节点的实例 `branch`,如示例 15-27 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-27/src/main.rs:there}}
|
||||
@ -95,7 +95,7 @@ Rust 的内存安全性保证使其难以意外地制造永远也不会被清理
|
||||
|
||||
所以 `parent` 使用 `Weak<T>` 类型而不是 `Rc<T>`,具体来说是 `RefCell<Weak<Node>>`。现在 `Node` 结构体定义看起来像这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-28/src/main.rs:here}}
|
||||
@ -103,7 +103,7 @@ Rust 的内存安全性保证使其难以意外地制造永远也不会被清理
|
||||
|
||||
这样,一个节点就能够引用其父节点,但不拥有其父节点。在示例 15-28 中,我们更新 `main` 来使用新定义以便 `leaf` 节点可以通过 `branch` 引用其父节点:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-28/src/main.rs:there}}
|
||||
@ -135,7 +135,7 @@ children: RefCell { value: [] } }] } })
|
||||
|
||||
让我们通过创建了一个新的内部作用域并将 `branch` 的创建放入其中,来观察 `Rc<Node>` 实例的 `strong_count` 和 `weak_count` 值的变化。这会展示当 `branch` 创建和离开作用域被丢弃时会发生什么。这些修改如示例 15-29 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-29/src/main.rs:here}}
|
||||
|
@ -19,7 +19,7 @@ Rust 尝试减轻使用线程的负面影响。不过在多线程上下文中编
|
||||
|
||||
为了创建一个新线程,需要调用 `thread::spawn` 函数并传递一个闭包(第十三章学习了闭包),并在其中包含希望在新线程运行的代码。示例 16-1 中的例子在主线程打印了一些文本而另一些文本则由新线程打印:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-01/src/main.rs}}
|
||||
@ -51,7 +51,7 @@ hi number 5 from the spawned thread!
|
||||
|
||||
可以通过将 `thread::spawn` 的返回值储存在变量中来修复新建线程部分没有执行或者完全没有执行的问题。`thread::spawn` 的返回值类型是 `JoinHandle`。`JoinHandle` 是一个拥有所有权的值,当对其调用 `join` 方法时,它会等待其线程结束。示例 16-2 展示了如何使用示例 16-1 中创建的线程的 `JoinHandle` 并调用 `join` 来确保新建线程在 `main` 退出前结束运行:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-02/src/main.rs}}
|
||||
@ -81,7 +81,7 @@ hi number 9 from the spawned thread!
|
||||
|
||||
不过让我们看看将 `handle.join()` 移动到 `main` 中 `for` 循环之前会发生什么,如下:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/no-listing-01-join-too-early/src/main.rs}}
|
||||
@ -115,7 +115,7 @@ hi number 4 from the main thread!
|
||||
|
||||
注意示例 16-1 中传递给 `thread::spawn` 的闭包并没有任何参数:并没有在新建线程代码中使用任何主线程的数据。为了在新建线程中使用来自于主线程的数据,需要新建线程的闭包获取它需要的值。示例 16-3 展示了一个尝试在主线程中创建一个 vector 并用于新建线程的例子,不过这么写还不能工作,如下所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-03/src/main.rs}}
|
||||
@ -133,7 +133,7 @@ Rust 会 **推断** 如何捕获 `v`,因为 `println!` 只需要 `v` 的引用
|
||||
|
||||
示例 16-4 展示了一个 `v` 的引用很有可能不再有效的场景:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-04/src/main.rs}}
|
||||
@ -154,7 +154,7 @@ help: to force the closure to take ownership of `v` (and any other referenced va
|
||||
|
||||
通过在闭包之前增加 `move` 关键字,我们强制闭包获取其使用的值的所有权,而不是任由 Rust 推断它应该借用值。示例 16-5 中展示的对示例 16-3 代码的修改,可以按照我们的预期编译并运行:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-05/src/main.rs}}
|
||||
|
@ -13,7 +13,7 @@ Rust 中一个实现消息传递并发的主要工具是 **信道**(_channel_
|
||||
|
||||
首先,在示例 16-6 中,创建了一个信道但没有做任何事。注意这还不能编译,因为 Rust 不知道我们想要在信道中发送什么类型:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-06/src/main.rs}}
|
||||
@ -27,7 +27,7 @@ Rust 中一个实现消息传递并发的主要工具是 **信道**(_channel_
|
||||
|
||||
让我们将发送端移动到一个新建线程中并发送一个字符串,这样新建线程就可以和主线程通讯了,如示例 16-7 所示。这类似于在河的上游扔下一只橡皮鸭或从一个线程向另一个线程发送聊天信息:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-07/src/main.rs}}
|
||||
@ -41,7 +41,7 @@ Rust 中一个实现消息传递并发的主要工具是 **信道**(_channel_
|
||||
|
||||
在示例 16-8 中,我们在主线程中从信道的接收端获取值。这类似于在河的下游捞起橡皮鸭或接收聊天信息:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-08/src/main.rs}}
|
||||
@ -67,7 +67,7 @@ Got: hi
|
||||
|
||||
所有权规则在消息传递中扮演了重要角色,其有助于我们编写安全的并发代码。防止并发编程中的错误是在 Rust 程序中考虑所有权的一大优势。现在让我们做一个试验来看看信道与所有权如何一同协作以避免产生问题:我们将尝试在新建线程中的信道中发送完 `val` 值 **之后** 再使用它。尝试编译示例 16-9 中的代码并看看为何这是不允许的:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-09/src/main.rs}}
|
||||
@ -87,7 +87,7 @@ Got: hi
|
||||
|
||||
示例 16-8 中的代码可以编译和运行,不过它并没有明确的告诉我们两个独立的线程通过信道相互通讯。示例 16-10 则有一些改进会证明示例 16-8 中的代码是并发执行的:新建线程现在会发送多个消息并在每个消息之间暂停一秒钟。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-10/src/main.rs}}
|
||||
@ -114,7 +114,7 @@ Got: thread
|
||||
|
||||
之前我们提到了`mpsc`是 _multiple producer, single consumer_ 的缩写。可以运用 `mpsc` 来扩展示例 16-10 中的代码来创建向同一接收者发送值的多个线程。这可以通过克隆信道的发送端来做到,如示例 16-11 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-11/src/main.rs:here}}
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
作为展示如何使用互斥器的例子,让我们从在单线程上下文使用互斥器开始,如示例 16-12 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-12/src/main.rs}}
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
现在让我们尝试使用 `Mutex<T>` 在多个线程间共享值。我们将启动十个线程,并在各个线程中对同一个计数器值加一,这样计数器将从 0 变为 10。示例 16-13 中的例子会出现编译错误,而我们将通过这些错误来学习如何使用 `Mutex<T>`,以及 Rust 又是如何帮助我们正确使用的。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-13/src/main.rs}}
|
||||
@ -74,7 +74,7 @@
|
||||
|
||||
在第十五章中,通过使用智能指针 `Rc<T>` 来创建引用计数的值,以便拥有多所有者。让我们在这也这么做看看会发生什么。将示例 16-14 中的 `Mutex<T>` 封装进 `Rc<T>` 中并在将所有权移入线程之前克隆了 `Rc<T>`。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-14/src/main.rs}}
|
||||
@ -100,7 +100,7 @@
|
||||
|
||||
回到之前的例子:`Arc<T>` 和 `Rc<T>` 有着相同的 API,所以修改程序中的 `use` 行和 `new` 调用。示例 16-15 中的代码最终可以编译和运行:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-15/src/main.rs}}
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
就像我们在第七章讨论的那样:可以使用 `pub` 关键字来决定模块、类型、函数和方法是公有的,而默认情况下其他一切都是私有的。比如,我们可以定义一个包含一个 `i32` 类型 vector 的结构体 `AveragedCollection `。结构体也可以有一个字段,该字段保存了 vector 中所有值的平均值。这样,希望知道结构体中的 vector 的平均值的人可以随时获取它,而无需自己计算。换句话说,`AveragedCollection` 会为我们缓存平均值结果。示例 17-1 有 `AveragedCollection` 结构体的定义:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-01/src/lib.rs}}
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
注意,结构体自身被标记为 `pub`,这样其他代码就可以使用这个结构体,但是在结构体内部的字段仍然是私有的。这是非常重要的,因为我们希望保证变量被增加到列表或者被从列表删除时,也会同时更新平均值。可以通过在结构体上实现 `add`、`remove` 和 `average` 方法来做到这一点,如示例 17-2 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-02/src/lib.rs:here}}
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
示例 17-3 展示了如何定义一个带有 `draw` 方法的 trait `Draw`:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-03/src/lib.rs}}
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
因为第十章已经讨论过如何定义 trait,其语法看起来应该比较眼熟。接下来就是新内容了:示例 17-4 定义了一个存放了名叫 `components` 的 vector 的结构体 `Screen`。这个 vector 的类型是 `Box<dyn Draw>`,此为一个 trait 对象:它是 `Box` 中任何实现了 `Draw` trait 的类型的替身。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-04/src/lib.rs:here}}
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
在 `Screen` 结构体上,我们将定义一个 `run` 方法,该方法会对其 `components` 上的每一个组件调用 `draw` 方法,如示例 17-5 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-05/src/lib.rs:here}}
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
这与定义使用了带有 trait bound 的泛型类型参数的结构体不同。泛型类型参数一次只能替代一个具体类型,而 trait 对象则允许在运行时替代多种具体类型。例如,可以定义 `Screen` 结构体来使用泛型和 trait bound,如示例 17-6 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-06/src/lib.rs:here}}
|
||||
@ -66,7 +66,7 @@
|
||||
|
||||
现在来增加一些实现了 `Draw` trait 的类型。我们将提供 `Button` 类型。再一次重申,真正实现 GUI 库超出了本书的范畴,所以 `draw` 方法体中不会有任何有意义的实现。为了想象一下这个实现看起来像什么,一个 `Button` 结构体可能会拥有 `width`、`height` 和 `label` 字段,如示例 17-7 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-07/src/lib.rs:here}}
|
||||
@ -78,7 +78,7 @@
|
||||
|
||||
如果一些库的使用者决定实现一个包含 `width`、`height` 和 `options` 字段的结构体 `SelectBox`,并且也为其实现了 `Draw` trait,如示例 17-8 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-08/src/main.rs:here}}
|
||||
@ -88,7 +88,7 @@
|
||||
|
||||
库使用者现在可以在他们的 `main` 函数中创建一个 `Screen` 实例。至此可以通过将 `SelectBox` 和 `Button` 放入 `Box<T>` 转变为 trait 对象来增加组件。接着可以调用 `Screen` 的 `run` 方法,它会调用每个组件的 `draw` 方法。示例 17-9 展示了这个实现:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-09/src/main.rs:here}}
|
||||
@ -104,7 +104,7 @@
|
||||
|
||||
例如,示例 17-10 展示了当创建一个使用 `String` 做为其组件的 `Screen` 时发生的情况:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-10/src/main.rs}}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
示例 17-11 展示这个工作流的代码形式:这是一个我们将要在一个叫做 `blog` 的库 crate 中实现的 API 的示例。这段代码还不能编译,因为还未实现 `blog`。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-11/src/main.rs:all}}
|
||||
@ -37,7 +37,7 @@
|
||||
|
||||
让我们开始实现这个库吧!我们知道需要一个公有 `Post` 结构体来存放一些文本,所以让我们从结构体的定义和一个创建 `Post` 实例的公有关联函数 `new` 开始,如示例 17-12 所示。还需定义一个私有 trait `State`。`Post` 将在私有字段 `state` 中存放一个 `Option<T>` 类型的 trait 对象 `Box<dyn State>`。稍后将会看到为何 `Option<T>` 是必须的。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-12/src/lib.rs}}
|
||||
@ -53,7 +53,7 @@
|
||||
|
||||
在示例 17-11 中,展示了我们希望能够调用一个叫做 `add_text` 的方法并向其传递一个 `&str` 来将文本增加到博文的内容中。选择实现为一个方法而不是将 `content` 字段暴露为 `pub` 。这意味着之后可以实现一个方法来控制 `content` 字段如何被读取。`add_text` 方法是非常直观的,让我们在示例 17-13 的 `impl Post` 块中增加一个实现:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-13/src/lib.rs:here}}
|
||||
@ -67,7 +67,7 @@
|
||||
|
||||
即使调用 `add_text` 并向博文增加一些内容之后,我们仍然希望 `content` 方法返回一个空字符串 slice,因为博文仍然处于草案状态,如示例 17-11 的第 8 行所示。现在让我们使用能满足要求的最简单的方式来实现 `content` 方法:总是返回一个空字符串 slice。当实现了将博文状态改为发布的能力之后将改变这一做法。但是目前博文只能是草案状态,这意味着其内容应该总是空的。示例 17-14 展示了这个占位符实现:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-14/src/lib.rs:here}}
|
||||
@ -81,7 +81,7 @@
|
||||
|
||||
接下来需要增加请求审核博文的功能,这应当将其状态由 `Draft` 改为 `PendingReview`。示例 17-15 展示了这个代码:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-15/src/lib.rs:here}}
|
||||
@ -107,7 +107,7 @@
|
||||
|
||||
`approve` 方法将与 `request_review` 方法类似:它会将 `state` 设置为审核通过时应处于的状态,如示例 17-16 所示。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-16/src/lib.rs:here}}
|
||||
@ -121,7 +121,7 @@
|
||||
|
||||
现在需要更新 `Post` 的 `content` 方法。我们希望 `content` 根据 `Post` 的当前状态返回值,所以需要 `Post` 代理一个定义于 `state` 上的 `content` 方法,如实例 17-17 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-17/src/lib.rs:here}}
|
||||
@ -137,7 +137,7 @@
|
||||
|
||||
接着我们就有了一个 `&Box<dyn State>`,当调用其 `content` 时,Deref 强制转换会作用于 `&` 和 `Box` ,这样最终会调用实现了 `State` trait 的类型的 `content` 方法。这意味着需要为 `State` trait 定义增加 `content`,这也是放置根据所处状态返回什么内容的逻辑的地方,如示例 17-18 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-18/src/lib.rs:here}}
|
||||
@ -179,7 +179,7 @@
|
||||
|
||||
让我们考虑一下示例 17-11 中 `main` 的第一部分:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-11/src/main.rs:here}}
|
||||
@ -187,7 +187,7 @@
|
||||
|
||||
我们仍然希望能够使用 `Post::new` 创建一个新的草案博文,并能够增加博文的内容。不过不同于存在一个草案博文时返回空字符串的 `content` 方法,我们将使草案博文完全没有 `content` 方法。这样如果尝试获取草案博文的内容,将会得到一个方法不存在的编译错误。这使得我们不可能在生产环境意外显示出草案博文的内容,因为这样的代码甚至就不能编译。示例 17-19 展示了 `Post` 结构体、`DraftPost` 结构体以及各自的方法的定义:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-19/src/lib.rs}}
|
||||
@ -205,7 +205,7 @@
|
||||
|
||||
那么如何得到发布的博文呢?我们希望强制执行的规则是草案博文在可以发布之前必须被审核通过。等待审核状态的博文应该仍然不会显示任何内容。让我们通过增加另一个结构体 `PendingReviewPost` 来实现这个限制,在 `DraftPost` 上定义 `request_review` 方法来返回 `PendingReviewPost`,并在 `PendingReviewPost` 上定义 `approve` 方法来返回 `Post`,如示例 17-20 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-20/src/lib.rs:here}}
|
||||
@ -217,7 +217,7 @@
|
||||
|
||||
这也意味着不得不对 `main` 做出一些小的修改。因为 `request_review` 和 `approve` 返回新实例而不是修改被调用的结构体,所以我们需要增加更多的 `let post = ` 覆盖赋值来保存返回的实例。也不再能断言草案和等待审核的博文的内容为空字符串了,我们也不再需要他们:不能编译尝试使用这些状态下博文内容的代码。更新后的 `main` 的代码如示例 17-21 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch17-oop/listing-17-21/src/main.rs}}
|
||||
@ -235,7 +235,7 @@
|
||||
|
||||
阅读本章后,不管你是否认为 Rust 是一个面向对象语言,现在你都见识了 trait 对象是一个 Rust 中获取部分面向对象功能的方法。动态分发可以通过牺牲少量运行时性能来为你的代码提供一些灵活性。这些灵活性可以用来实现有助于代码可维护性的面向对象模式。Rust 也有像所有权这样不同于面向对象语言的功能。面向对象模式并不总是利用 Rust 优势的最好方式,但也是可用的选项。
|
||||
|
||||
接下来,让我们看看另一个提供了多样灵活性的 Rust 功能:模式。贯穿全书的模式, 我们已经和它们打过照面了,但并没有见识过它们的全部本领。让我们开始探索吧!
|
||||
接下来,让我们看看另一个提供了多样灵活性的 Rust 功能:模式。贯穿全书的模式,我们已经和它们打过照面了,但并没有见识过它们的全部本领。让我们开始探索吧!
|
||||
|
||||
[more-info-than-rustc]: ch09-03-to-panic-or-not-to-panic.html#当我们比编译器知道更多的情况
|
||||
[macros]: ch19-06-macros.html#宏
|
||||
|
@ -30,7 +30,7 @@ match VALUE {
|
||||
|
||||
示例 18-1 中的代码展示了一系列针对不同条件的检查来决定背景颜色应该是什么。为了达到这个例子的目的,我们创建了硬编码值的变量,在真实程序中则可能由询问用户获得。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-01/src/main.rs}}
|
||||
@ -132,7 +132,7 @@ let PATTERN = EXPRESSION;
|
||||
|
||||
`x` 部分就是一个模式!类似于之前对 `let` 所做的,可以在函数参数中匹配元组。列表 18-7 将传递给函数的元组拆分为值:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-07/src/main.rs}}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
命名变量是匹配任何值的不可反驳模式,这在之前已经使用过数次。然而当其用于 `match` 表达式时情况会有些复杂。因为 `match` 会开始一个新作用域,`match` 表达式中作为模式的一部分声明的变量会覆盖 `match` 结构之外的同名变量,与所有变量一样。在示例 18-11 中,声明了一个值为 `Some(5)` 的变量 `x` 和一个值为 `10` 的变量 `y`。接着在值 `x` 上创建了一个 `match` 表达式。观察匹配分支中的模式和结尾的 `println!`,并在运行此代码或进一步阅读之前推断这段代码会打印什么。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-11/src/main.rs:here}}
|
||||
@ -75,7 +75,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
示例 18-12 展示带有两个字段 `x` 和 `y` 的结构体 `Point`,可以通过带有模式的 `let` 语句将其分解:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-12/src/main.rs}}
|
||||
@ -87,7 +87,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
因为变量名匹配字段名是常见的,同时因为 `let Point { x: x, y: y } = p;` 包含了很多重复,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。示例 18-13 展示了与示例 18-12 有着相同行为的代码,不过 `let` 模式创建的变量为 `x` 和 `y` 而不是 `a` 和 `b`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-13/src/main.rs}}
|
||||
@ -101,7 +101,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
示例 18-14 展示了一个 `match` 语句将 `Point` 值分成了三种情况:直接位于 `x` 轴上(此时 `y = 0` 为真)、位于 `y` 轴上(`x = 0`)或不在任何轴上的点。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-14/src/main.rs:here}}
|
||||
@ -119,7 +119,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
本书之前的部分曾经解构过枚举,比如第六章中示例 6-5 中解构了一个 `Option<i32>`。一个当时没有明确提到的细节是解构枚举的模式需要对应枚举所定义的储存数据的方式。让我们以示例 6-2 中的 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值,如示例 18-15 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-15/src/main.rs}}
|
||||
@ -169,7 +169,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
我们已经使用过下划线(`_`)作为匹配但不绑定任何值的通配符模式了。虽然 `_` 模式作为 `match` 表达式最后的分支特别有用,也可以将其用于任意模式,包括函数参数中,如示例 18-17 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-17/src/main.rs}}
|
||||
@ -209,7 +209,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 bug。但是有时创建一个还未使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头。示例 18-20 中创建了两个未使用变量,不过当编译代码时只会得到其中一个的警告:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-20/src/main.rs}}
|
||||
@ -251,7 +251,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
`..` 会扩展为所需要的值的数量。示例 18-24 展示了元组中 `..` 的应用:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-24/src/main.rs}}
|
||||
@ -263,7 +263,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
然而使用 `..` 必须是无歧义的。如果期望匹配和忽略的值是不明确的,Rust 会报错。示例 18-25 展示了一个带有歧义的 `..` 例子,因此其不能编译:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-25/src/main.rs}}
|
||||
@ -299,7 +299,7 @@ Rust 不可能决定在元组中匹配 `second` 值之前应该忽略多少个
|
||||
|
||||
在示例 18-11 中,我们提到可以使用匹配守卫来解决模式中变量覆盖的问题,那里 `match` 表达式的模式中新建了一个变量而不是使用 `match` 之外的同名变量。新变量意味着不能够测试外部变量的值。示例 18-27 展示了如何使用匹配守卫修复这个问题。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-27/src/main.rs}}
|
||||
|
@ -161,7 +161,7 @@ Rust 的借用检查器不能理解我们要借用这个 slice 的两个不同
|
||||
|
||||
示例 19-8 展示了如何集成 C 标准库中的 `abs` 函数。`extern` 块中声明的函数在 Rust 代码中总是不安全的。因为其他语言不会强制执行 Rust 的规则且 Rust 无法检查它们,所以确保其安全是程序员的责任:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-08/src/main.rs}}
|
||||
@ -192,7 +192,7 @@ Rust 的借用检查器不能理解我们要借用这个 slice 的两个不同
|
||||
|
||||
全局变量在 Rust 中被称为 **静态**(*static*)变量。示例 19-9 展示了一个拥有字符串 slice 值的静态变量的声明和应用:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-09/src/main.rs}}
|
||||
@ -206,7 +206,7 @@ Rust 的借用检查器不能理解我们要借用这个 slice 的两个不同
|
||||
|
||||
常量与静态变量的另一个区别在于静态变量可以是可变的。访问和修改可变静态变量都是 **不安全** 的。示例 19-10 展示了如何声明、访问和修改名为 `COUNTER` 的可变静态变量:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-10/src/main.rs}}
|
||||
|
@ -20,13 +20,13 @@
|
||||
|
||||
<span class="caption">示例 19-12: `Iterator` trait 的定义中带有关联类型 `Item`</span>
|
||||
|
||||
`Item` 是一个占位类型,同时 `next` 方法定义表明它返回 `Option<Self::Item>` 类型的值。这个 trait 的实现者会指定 `Item` 的具体类型,然而不管实现者指定何种类型, `next` 方法都会返回一个包含了此具体类型值的 `Option`。
|
||||
`Item` 是一个占位类型,同时 `next` 方法定义表明它返回 `Option<Self::Item>` 类型的值。这个 trait 的实现者会指定 `Item` 的具体类型,然而不管实现者指定何种类型,`next` 方法都会返回一个包含了此具体类型值的 `Option`。
|
||||
|
||||
关联类型看起来像一个类似泛型的概念,因为它允许定义一个函数而不指定其可以处理的类型。那么为什么要使用关联类型呢?
|
||||
|
||||
让我们通过一个在第十三章中出现的 `Counter` 结构体上实现 `Iterator` trait 的例子来检视其中的区别。在示例 13-21 中,指定了 `Item` 的类型为 `u32`:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-13-21-reproduced/src/lib.rs:ch19}}
|
||||
@ -52,7 +52,7 @@
|
||||
|
||||
Rust 并不允许创建自定义运算符或重载任意运算符,不过 `std::ops` 中所列出的运算符和相应的 trait 可以通过实现运算符相关 trait 来重载。例如,示例 19-14 中展示了如何在 `Point` 结构体上实现 `Add` trait 来重载 `+` 运算符,这样就可以将两个 `Point` 实例相加了:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-14/src/main.rs}}
|
||||
@ -78,7 +78,7 @@ trait Add<Rhs=Self> {
|
||||
|
||||
这里有两个存放不同单元值的结构体,`Millimeters` 和 `Meters`。(这种将现有类型简单封装进另一个结构体的方式被称为 **newtype 模式**(*newtype pattern*,之后的 [“为了类型安全和抽象而使用 newtype 模式”][newtype] 部分会详细介绍。)我们希望能够将毫米值与米值相加,并让 `Add` 的实现正确处理转换。可以为 `Millimeters` 实现 `Add` 并以 `Meters` 作为 `Rhs`,如示例 19-15 所示。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-15/src/lib.rs}}
|
||||
@ -103,7 +103,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
不过,当调用这些同名方法时,需要告诉 Rust 我们希望使用哪一个。考虑一下示例 19-16 中的代码,这里定义了 trait `Pilot` 和 `Wizard` 都拥有方法 `fly`。接着在一个本身已经实现了名为 `fly` 方法的类型 `Human` 上实现这两个 trait。每一个 `fly` 方法都进行了不同的操作:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-16/src/main.rs:here}}
|
||||
@ -113,7 +113,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
当调用 `Human` 实例的 `fly` 时,编译器默认调用直接实现在类型上的方法,如示例 19-17 所示。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-17/src/main.rs:here}}
|
||||
@ -125,7 +125,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
为了能够调用 `Pilot` trait 或 `Wizard` trait 的 `fly` 方法,我们需要使用更明显的语法以便能指定我们指的是哪个 `fly` 方法。这个语法展示在示例 19-18 中:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-18/src/main.rs:here}}
|
||||
@ -145,7 +145,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
然而,关联函数是 trait 的一部分,但没有 `self` 参数。当同一作用域的两个类型实现了同一 trait,Rust 就不能计算出我们期望的是哪一个类型,除非使用 **完全限定语法**(*fully qualified syntax*)。例如,拿示例 19-19 中的 `Animal` trait 来说,它有关联函数 `baby_name`,结构体 `Dog` 实现了 `Animal`,同时有关联函数 `baby_name` 直接定义于 `Dog` 之上:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-19/src/main.rs}}
|
||||
@ -163,7 +163,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
这并不是我们需要的。我们希望调用的是 `Dog` 上 `Animal` trait 实现那部分的 `baby_name` 函数,这样能够打印出 `A baby dog is called a puppy`。示例 19-18 中用到的技术在这并不管用;如果将 `main` 改为示例 19-20 中的代码,则会得到一个编译错误:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-20/src/main.rs:here}}
|
||||
@ -179,7 +179,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
为了消歧义并告诉 Rust 我们希望使用的是 `Dog` 的 `Animal` 实现,需要使用 **完全限定语法**,这是调用函数时最为明确的方式。示例 19-21 展示了如何使用完全限定语法:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-21/src/main.rs:here}}
|
||||
@ -217,7 +217,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
在 `outline_print` 的实现中,因为希望能够使用 `Display` trait 的功能,则需要说明 `OutlinePrint` 只能用于同时也实现了 `Display` 并提供了 `OutlinePrint` 需要的功能的类型。可以通过在 trait 定义中指定 `OutlinePrint: Display` 来做到这一点。这类似于为 trait 增加 trait bound。示例 19-22 展示了一个 `OutlinePrint` trait 的实现:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-22/src/main.rs:here}}
|
||||
@ -229,7 +229,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
让我们看看如果尝试在一个没有实现 `Display` 的类型上实现 `OutlinePrint` 会发生什么,比如 `Point` 结构体:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-02-impl-outlineprint-for-point/src/main.rs:here}}
|
||||
@ -243,7 +243,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
一旦在 `Point` 上实现 `Display` 并满足 `OutlinePrint` 要求的限制,比如这样:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-03-impl-display-for-point/src/main.rs:here}}
|
||||
@ -257,7 +257,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
|
||||
|
||||
例如,如果想要在 `Vec<T>` 上实现 `Display`,而孤儿规则阻止我们直接这么做,因为 `Display` trait 和 `Vec<T>` 都定义于我们的 crate 之外。可以创建一个包含 `Vec<T>` 实例的 `Wrapper` 结构体,接着可以如列表 19-23 那样在 `Wrapper` 上实现 `Display` 并使用 `Vec<T>` 的值:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-23/src/main.rs}}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
我们讨论过了如何向函数传递闭包;也可以向函数传递常规函数!这在我们希望传递已经定义的函数而不是重新定义闭包作为参数时很有用。通过函数指针允许我们使用函数作为另一个函数的参数。函数的类型是 `fn` (使用小写的 “f” )以免与 `Fn` 闭包 trait 相混淆。`fn` 被称为 **函数指针**(*function pointer*)。指定参数为函数指针的语法类似于闭包,如示例 19-27 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-27/src/main.rs}}
|
||||
|
@ -38,7 +38,7 @@ let v: Vec<u32> = vec![1, 2, 3];
|
||||
|
||||
在示例 19-28 中展示了一个 `vec!` 稍微简化的定义。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-28/src/lib.rs}}
|
||||
@ -91,7 +91,7 @@ let v: Vec<u32> = vec![1, 2, 3];
|
||||
|
||||
创建过程宏时,其定义必须驻留在它们自己的具有特殊 crate 类型的 crate 中。这么做出于复杂的技术原因,将来我们希望能够消除这些限制。使用这些宏需采用类似示例 19-29 所示的代码形式,其中 `some_attribute` 是一个使用特定宏的占位符。
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
use proc_macro;
|
||||
@ -111,7 +111,7 @@ pub fn some_name(input: TokenStream) -> TokenStream {
|
||||
|
||||
让我们创建一个 `hello_macro` crate,其包含名为 `HelloMacro` 的 trait 和关联函数 `hello_macro`。不同于让 crate 的用户为其每一个类型实现 `HelloMacro` trait,我们将会提供一个过程式宏以便用户可以使用 `#[derive(HelloMacro)]` 注解他们的类型来得到 `hello_macro` 函数的默认实现。该默认实现会打印 `Hello, Macro! My name is TypeName!`,其中 `TypeName` 为定义了 trait 的类型名。换言之,我们会创建一个 crate,使程序员能够写类似示例 19-30 中的代码。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-30/src/main.rs}}
|
||||
@ -127,7 +127,7 @@ $ cargo new hello_macro --lib
|
||||
|
||||
接下来,会定义 `HelloMacro` trait 以及其关联函数:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/hello_macro/src/lib.rs}}
|
||||
@ -153,7 +153,7 @@ $ cargo new hello_macro_derive --lib
|
||||
|
||||
我们需要声明 `hello_macro_derive` crate 是过程宏 (proc-macro) crate。我们还需要 `syn` 和 `quote` crate 中的功能,正如你即将看到的,需要将他们加到依赖中。将下面的代码加入到 `hello_macro_derive` 的 *Cargo.toml* 文件中。
|
||||
|
||||
<span class="filename">文件名: hello_macro_derive/Cargo.toml</span>
|
||||
<span class="filename">文件名:hello_macro_derive/Cargo.toml</span>
|
||||
|
||||
```toml
|
||||
{{#include ../listings/ch19-advanced-features/listing-19-31/hello_macro/hello_macro_derive/Cargo.toml:6:12}}
|
||||
@ -161,7 +161,7 @@ $ cargo new hello_macro_derive --lib
|
||||
|
||||
为定义一个过程式宏,请将示例 19-31 中的代码放在 `hello_macro_derive` crate 的 *src/lib.rs* 文件里面。注意这段代码在我们添加 `impl_hello_macro` 函数的定义之前是无法编译的。
|
||||
|
||||
<span class="filename">文件名: hello_macro_derive/src/lib.rs</span>
|
||||
<span class="filename">文件名:hello_macro_derive/src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-31/hello_macro/hello_macro_derive/src/lib.rs}}
|
||||
@ -214,7 +214,7 @@ DeriveInput {
|
||||
|
||||
现在我们有了将注解的 Rust 代码从 `TokenStream` 转换为 `DeriveInput` 实例的代码,让我们来创建在注解类型上实现 `HelloMacro` trait 的代码,如示例 19-33 所示。
|
||||
|
||||
<span class="filename">文件名: hello_macro_derive/src/lib.rs</span>
|
||||
<span class="filename">文件名:hello_macro_derive/src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-33/hello_macro/hello_macro_derive/src/lib.rs:here}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# 最后的项目: 构建多线程 web server
|
||||
# 最后的项目:构建多线程 web server
|
||||
|
||||
> [ch20-00-final-project-a-web-server.md](https://github.com/rust-lang/book/blob/main/src/ch20-00-final-project-a-web-server.md)
|
||||
> <br>
|
||||
|
@ -22,7 +22,7 @@ $ cd hello
|
||||
|
||||
并在 `src/main.rs` 输入示例 20-1 中的代码作为开始。这段代码会在地址 `127.0.0.1:7878` 上监听传入的 TCP 流。当获取到传入的流,它会打印出 `Connection established!`:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-01/src/main.rs}}
|
||||
@ -59,7 +59,7 @@ Connection established!
|
||||
|
||||
让我们实现读取来自浏览器请求的功能!为了分离获取连接和接下来对连接的操作的相关内容,我们将开始一个新函数来处理连接。在这个新的 `handle_connection` 函数中,我们从 TCP 流中读取数据并打印出来以便观察浏览器发送过来的数据。将代码修改为如示例 20-2 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-02/src/main.rs}}
|
||||
@ -142,7 +142,7 @@ HTTP/1.1 200 OK\r\n\r\n
|
||||
|
||||
状态码 200 是一个标准的成功响应。这些文本是一个微型的成功 HTTP 响应。让我们将这些文本写入流作为成功请求的响应!在 `handle_connection` 函数中,我们需要去掉打印请求数据的 `println!`,并替换为示例 20-3 中的代码:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-03/src/main.rs:here}}
|
||||
@ -160,7 +160,7 @@ HTTP/1.1 200 OK\r\n\r\n
|
||||
|
||||
让我们实现不只是返回空页面的功能。在项目根目录创建一个新文件,*hello.html* —— 也就是说,不是在 `src` 目录。在此可以放入任何你期望的 HTML;列表 20-4 展示了一个可能的文本:
|
||||
|
||||
<span class="filename">文件名: hello.html</span>
|
||||
<span class="filename">文件名:hello.html</span>
|
||||
|
||||
```html
|
||||
{{#include ../listings/ch20-web-server/listing-20-04/hello.html}}
|
||||
@ -170,7 +170,7 @@ HTTP/1.1 200 OK\r\n\r\n
|
||||
|
||||
这是一个极小化的 HTML5 文档,它有一个标题和一小段文本。为了在 server 接受请求时返回它,需要如示例 20-5 所示修改 `handle_connection` 来读取 HTML 文件,将其加入到响应的 body 中,并发送:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-05/src/main.rs:here}}
|
||||
@ -190,7 +190,7 @@ HTTP/1.1 200 OK\r\n\r\n
|
||||
|
||||
目前我们的 web server 不管客户端请求什么都会返回相同的 HTML 文件。让我们增加在返回 HTML 文件前检查浏览器是否请求 */*,并在其请求任何其他内容时返回错误的功能。为此需要如示例 20-6 那样修改 `handle_connection`。新代码接收到的请求的内容与已知的 */* 请求的一部分做比较,并增加了 `if` 和 `else` 块来区别处理请求:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-06/src/main.rs:here}}
|
||||
@ -206,7 +206,7 @@ HTTP/1.1 200 OK\r\n\r\n
|
||||
|
||||
现在向示例 20-7 的 `else` 块增加代码来返回一个带有 404 状态码的响应,这代表了所请求的内容没有找到。接着也会返回一个 HTML 向浏览器终端用户表明此意:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-07/src/main.rs:here}}
|
||||
@ -216,7 +216,7 @@ HTTP/1.1 200 OK\r\n\r\n
|
||||
|
||||
这里,响应的状态行有状态码 404 和原因短语 `NOT FOUND`。仍然没有返回任何 header,而其 body 将是 *404.html* 文件中的 HTML。需要在 *hello.html* 同级目录创建 *404.html* 文件作为错误页面;这一次也可以随意使用任何 HTML 或使用示例 20-8 中的示例 HTML:
|
||||
|
||||
<span class="filename">文件名: 404.html</span>
|
||||
<span class="filename">文件名:404.html</span>
|
||||
|
||||
```html
|
||||
{{#include ../listings/ch20-web-server/listing-20-08/404.html}}
|
||||
@ -230,7 +230,7 @@ HTTP/1.1 200 OK\r\n\r\n
|
||||
|
||||
目前 `if` 和 `else` 块中的代码有很多的重复:他们都读取文件并将其内容写入流。唯一的区别是状态行和文件名。为了使代码更为简明,将这些区别分别提取到一行 `if` 和 `else` 中,对状态行和文件名变量赋值;然后在读取文件和写入响应的代码中无条件的使用这些变量。重构后取代了大段 `if` 和 `else` 块代码后的结果如示例 20-9 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-09/src/main.rs:here}}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
让我们看看一个慢请求如何影响当前 server 实现中的其他请求。示例 20-10 通过模拟慢响应实现了 */sleep* 请求处理,它会使 server 在响应之前休眠五秒。
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-10/src/main.rs:here}}
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
首先,让我们探索一下为每一个连接都创建一个线程的代码看起来如何。这并不是最终方案,因为正如之前讲到的它会潜在的分配无限的线程,不过这是一个开始。示例 20-11 展示了 `main` 的改变,它在 `for` 循环中为每一个流分配了一个新线程进行处理:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,no_run
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-11/src/main.rs:here}}
|
||||
@ -58,7 +58,7 @@
|
||||
|
||||
我们期望线程池以类似且熟悉的方式工作,以便从线程切换到线程池并不会对使用该 API 的代码做出较大的修改。示例 20-12 展示我们希望用来替换 `thread::spawn` 的 `ThreadPool` 结构体的假想接口:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-12/src/main.rs:here}}
|
||||
@ -80,7 +80,7 @@
|
||||
|
||||
创建 *src/lib.rs* 文件,它包含了目前可用的最简单的 `ThreadPool` 定义:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/lib.rs}}
|
||||
@ -88,7 +88,7 @@
|
||||
|
||||
接着创建一个新目录,*src/bin*,并将二进制 crate 根文件从 *src/main.rs* 移动到 *src/bin/main.rs*。这使得库 crate 成为 *hello* 目录的主要 crate;不过仍然可以使用 `cargo run` 运行 *src/bin/main.rs* 二进制文件。移动了 *main.rs* 文件之后,修改 *src/bin/main.rs* 文件开头加入如下代码来引入库 crate 并将 `ThreadPool` 引入作用域:
|
||||
|
||||
<span class="filename">文件名: src/bin/main.rs</span>
|
||||
<span class="filename">文件名:src/bin/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/bin/main.rs:here}}
|
||||
@ -102,7 +102,7 @@
|
||||
|
||||
这告诉我们下一步是为 `ThreadPool` 创建一个叫做 `new` 的关联函数。我们还知道 `new` 需要有一个参数可以接受 `4`,而且 `new` 应该返回 `ThreadPool` 实例。让我们实现拥有此特征的最小化 `new` 函数:
|
||||
|
||||
<span class="filename">文件夹: src/lib.rs</span>
|
||||
<span class="filename">文件夹:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-02-impl-threadpool-new/src/lib.rs}}
|
||||
@ -132,7 +132,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
`F` 还有 trait bound `Send` 和生命周期绑定 `'static`,这对我们的情况也是有意义的:需要 `Send` 来将闭包从一个线程转移到另一个线程,而 `'static` 是因为并不知道线程会执行多久。让我们编写一个使用带有这些 bound 的泛型参数 `F` 的 `ThreadPool` 的 `execute` 方法:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-03-define-execute/src/lib.rs:here}}
|
||||
@ -154,7 +154,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
这里仍然存在警告是因为其并没有对 `new` 和 `execute` 的参数做任何操作。让我们用期望的行为来实现这些函数。以考虑 `new` 作为开始。之前选择使用无符号类型作为 `size` 参数的类型,因为线程数为负的线程池没有意义。然而,线程数为零的线程池同样没有意义,不过零是一个完全有效的 `u32` 值。让我们增加在返回 `ThreadPool` 实例之前检查 `size` 是否大于零的代码,并使用 `assert!` 宏在得到零时 panic,如示例 20-13 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-13/src/lib.rs:here}}
|
||||
@ -186,7 +186,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
示例 20-14 中的代码可以编译,不过实际上还并没有创建任何线程。我们改变了 `ThreadPool` 的定义来存放一个 `thread::JoinHandle<()>` 的 vector 实例,使用 `size` 容量来初始化,并设置一个 `for` 循环了来运行创建线程的代码,并返回包含这些线程的 `ThreadPool` 实例:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,not_desired_behavior
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-14/src/lib.rs:here}}
|
||||
@ -219,7 +219,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
准备好了吗?示例 20-15 就是一个做出了这些修改的例子:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-15/src/lib.rs:here}}
|
||||
@ -249,7 +249,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
让我们以在 `ThreadPool::new` 中创建信道并让 `ThreadPool` 实例充当发送端开始,如示例 20-16 所示。`Job` 是将在信道中发出的类型,目前它是一个没有任何内容的结构体:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-16/src/lib.rs:here}}
|
||||
@ -261,7 +261,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
让我们尝试在线程池创建每个 worker 时将信道的接收端传递给他们。须知我们希望在 worker 所分配的线程中使用信道的接收端,所以将在闭包中引用 `receiver` 参数。示例 20-17 中展示的代码还不能编译:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-17/src/lib.rs:here}}
|
||||
@ -283,7 +283,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
回忆一下第十六章讨论的线程安全智能指针,为了在多个线程间共享所有权并允许线程修改其值,需要使用 `Arc<Mutex<T>>`。`Arc` 使得多个 worker 拥有接收端,而 `Mutex` 则确保一次只有一个 worker 能从接收端得到任务。示例 20-18 展示了所需的修改:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-18/src/lib.rs:here}}
|
||||
@ -299,7 +299,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
最后让我们实现 `ThreadPool` 上的 `execute` 方法。同时也要修改 `Job` 结构体:它将不再是结构体,`Job` 将是一个有着 `execute` 接收到的闭包类型的 trait 对象的类型别名。第十九章 [“类型别名用来创建类型同义词”][creating-type-synonyms-with-type-aliases] 部分提到过,类型别名允许将长的类型变短。观察示例 20-19:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-19/src/lib.rs:here}}
|
||||
@ -311,7 +311,7 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
不过到此事情还没有结束!在 worker 中,传递给 `thread::spawn` 的闭包仍然还只是 **引用** 了信道的接收端。相反我们需要闭包一直循环,向信道的接收端请求任务,并在得到任务时执行他们。如示例 20-20 对 `Worker::new` 做出修改:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-20/src/lib.rs:here}}
|
||||
@ -372,7 +372,7 @@ Worker 2 got a job; executing.
|
||||
|
||||
在学习了第十八章的 `while let` 循环之后,你可能会好奇为何不能如此编写 worker 线程,如示例 20-21 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,not_desired_behavior
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-21/src/lib.rs:here}}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
现在开始为线程池实现 `Drop`。当线程池被丢弃时,应该 join 所有线程以确保他们完成其操作。示例 20-22 展示了 `Drop` 实现的第一次尝试;这些代码还不能够编译:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-22/src/lib.rs:here}}
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
为此需要更新 `Worker` 的定义为如下:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore,does_not_compile
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-04-update-worker-definition/src/lib.rs:here}}
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
让我们修复第二个错误,它指向 `Worker::new` 结尾的代码;当新建 `Worker` 时需要将 `thread` 值封装进 `Some`。做出如下改变以修复问题:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-05-fix-worker-new/src/lib.rs:here}}
|
||||
@ -54,7 +54,7 @@
|
||||
|
||||
第一个错误位于 `Drop` 实现中。之前提到过要调用 `Option` 上的 `take` 将 `thread` 移动出 `worker`。如下改变会修复问题:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/lib.rs:here}}
|
||||
@ -68,7 +68,7 @@
|
||||
|
||||
为了修复这个问题,修改线程既监听是否有 `Job` 运行也要监听一个应该停止监听并退出无限循环的信号。所以信道将发送这个枚举的两个成员之一而不是 `Job` 实例:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-07-define-message-enum/src/lib.rs:here}}
|
||||
@ -78,7 +78,7 @@
|
||||
|
||||
同时需要修改信道来使用 `Message` 类型值而不是 `Job`,如示例 20-23 所示:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-23/src/lib.rs:here}}
|
||||
@ -90,7 +90,7 @@
|
||||
|
||||
通过这些修改,代码再次能够编译并继续按照示例 20-20 之后相同的行为运行。不过还是会得到一个警告,因为并没有创建任何 `Terminate` 成员的消息。如示例 20-24 所示修改 `Drop` 实现来修复此问题:
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-24/src/lib.rs:here}}
|
||||
@ -106,7 +106,7 @@
|
||||
|
||||
为了实践这些代码,如示例 20-25 所示修改 `main` 在优雅停机 server 之前只接受两个请求:
|
||||
|
||||
<span class="filename">文件名: src/bin/main.rs</span>
|
||||
<span class="filename">文件名:src/bin/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch20-web-server/listing-20-25/src/bin/main.rs:here}}
|
||||
@ -148,13 +148,13 @@ Shutting down worker 3
|
||||
|
||||
如下是完整的代码参考:
|
||||
|
||||
<span class="filename">文件名: src/bin/main.rs</span>
|
||||
<span class="filename">文件名:src/bin/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-08-final-code/src/bin/main.rs}}
|
||||
```
|
||||
|
||||
<span class="filename">文件名: src/lib.rs</span>
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch20-web-server/no-listing-08-final-code/src/lib.rs}}
|
||||
|
Loading…
Reference in New Issue
Block a user