From 4e675ed58dd0935c080c1d50d4a99e06694ab340 Mon Sep 17 00:00:00 2001 From: KaiserY Date: Wed, 20 Nov 2019 00:43:49 +0800 Subject: [PATCH] check to ch04-03 --- src/SUMMARY.md | 2 +- src/ch03-01-variables-and-mutability.md | 10 +++- src/ch03-02-data-types.md | 58 ++++++++++++------- src/ch03-03-how-functions-work.md | 2 +- src/ch03-04-comments.md | 6 +- src/ch03-05-control-flow.md | 21 ++++--- src/ch04-01-what-is-ownership.md | 19 +++--- src/ch04-02-references-and-borrowing.md | 21 +++---- src/ch04-03-slices.md | 37 +++++------- ...referring-to-an-item-in-the-module-tree.md | 2 +- 10 files changed, 98 insertions(+), 80 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 2a41930..e87e1b1 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -40,7 +40,7 @@ - [使用包、Crate 和模块管理不断增长的项目](ch07-00-managing-growing-projects-with-packages-crates-and-modules.md) - [包和 crate](ch07-01-packages-and-crates.md) - [定义模块来控制作用域与私有性](ch07-02-defining-modules-to-control-scope-and-privacy.md) - - [路径来引用模块树中的项](ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md) + - [路径用于引用模块树中的项](ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md) - [使用 `use` 关键字将名称引入作用域](ch07-04-bringing-paths-into-scope-with-the-use-keyword.md) - [将模块分割进不同文件](ch07-05-separating-modules-into-different-files.md) diff --git a/src/ch03-01-variables-and-mutability.md b/src/ch03-01-variables-and-mutability.md index f5ec328..fb57379 100644 --- a/src/ch03-01-variables-and-mutability.md +++ b/src/ch03-01-variables-and-mutability.md @@ -2,7 +2,7 @@ > [ch03-01-variables-and-mutability.md](https://github.com/rust-lang/book/blob/master/src/ch03-01-variables-and-mutability.md) >
-> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37 +> commit d69b1058c660abfe1d274c58d39c06ebd5c96c47 第二章中提到过,变量默认是不可改变的(immutable)。这是推动你以充分利用 Rust 提供的安全性和简单并发性来编写代码的众多方式之一。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 拥抱不可变性的原因及方法,以及何时你不想使用不可变性。 @@ -78,7 +78,7 @@ The value of x is: 6 首先,不允许对常量使用 `mut`。常量不光默认不能变,它总是不能变。 -声明常量使用 `const` 关键字而不是 `let`,并且 *必须* 注明值的类型。在下一部分,“数据类型” 中会介绍类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。 +声明常量使用 `const` 关键字而不是 `let`,并且 *必须* 注明值的类型。在下一部分,[“数据类型”][data-types] 中会介绍类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。 常量可以在任何作用域中声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。 @@ -96,7 +96,7 @@ const MAX_POINTS: u32 = 100_000; ### 隐藏(Shadowing) -正如在第二章猜猜看游戏的 “比较猜测的数字和秘密数字” 中所讲,我们可以定义一个与之前变量同名的新变量,而新变量会 **隐藏** 之前的变量。Rustacean 们称之为第一个变量被第二个 **隐藏** 了,这意味着使用这个变量时会看到第二个值。可以用相同变量名称来隐藏一个变量,以及重复使用 `let` 关键字来多次隐藏,如下所示: +正如在第二章猜猜看游戏的 [“比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 中所讲,我们可以定义一个与之前变量同名的新变量,而新变量会 **隐藏** 之前的变量。Rustacean 们称之为第一个变量被第二个 **隐藏** 了,这意味着使用这个变量时会看到第二个值。可以用相同变量名称来隐藏一个变量,以及重复使用 `let` 关键字来多次隐藏,如下所示: 文件名: src/main.rs @@ -152,3 +152,7 @@ error[E0308]: mismatched types ``` 现在我们已经了解了变量如何工作,让我们看看变量可以拥有的更多数据类型。 + +[comparing-the-guess-to-the-secret-number]: +ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number +[data-types]: ch03-02-data-types.html#data-types \ No newline at end of file diff --git a/src/ch03-02-data-types.md b/src/ch03-02-data-types.md index 9a43665..2c16433 100644 --- a/src/ch03-02-data-types.md +++ b/src/ch03-02-data-types.md @@ -2,11 +2,11 @@ > [ch03-02-data-types.md](https://github.com/rust-lang/book/blob/master/src/ch03-02-data-types.md) >
-> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37 +> commit 6598d3abac05ed1d0c45db92466ea49346d05e40 在 Rust 中,每一个值都属于某一个 **数据类型**(*data type*),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。 -记住,Rust 是 **静态类型**(*statically typed*)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。当多种类型均有可能时,比如第二章的 “比较猜测的数字和秘密数字” 使用 `parse` 将 `String` 转换为数字时,必须增加类型注解,像这样: +记住,Rust 是 **静态类型**(*statically typed*)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。当多种类型均有可能时,比如第二章的 [“比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 使用 `parse` 将 `String` 转换为数字时,必须增加类型注解,像这样: ```rust let guess: u32 = "42".parse().expect("Not a number!"); @@ -38,14 +38,15 @@ error[E0282]: type annotations needed 表格 3-1: Rust 中的整型 | 长度 | 有符号 | 无符号 | -|--------|---------|----------| -| 8-bit | `i8` | `u8` | -| 16-bit | `i16` | `u16` | -| 32-bit | `i32` | `u32` | -| 64-bit | `i64` | `u64` | -| arch | `isize` | `usize` | +|---------|---------|----------| +| 8-bit | `i8` | `u8` | +| 16-bit | `i16` | `u16` | +| 32-bit | `i32` | `u32` | +| 64-bit | `i64` | `u64` | +| 128-bit | `i128` | `u128` | +| arch | `isize` | `usize` | -每一个变体都可以是有符号或无符号的,并有一个明确的大小。**有符号** 和 **无符号** 代表数字能否为负值,换句话说,数字是否需要有一个符号(有符号数),或者永远为正而不需要符号(无符号数)。这有点像在纸上书写数字:当需要考虑符号的时候,数字以加号或减号作为前缀;然而,可以安全地假设为正数时,加号前缀通常省略。有符号数以补码形式(two’s complement representation)存储(如果你不清楚这是什么,可以在网上搜索;对其的解释超出了本书的范畴)。 +每一个变体都可以是有符号或无符号的,并有一个明确的大小。**有符号** 和 **无符号** 代表数字能否为负值,换句话说,数字是否需要有一个符号(有符号数),或者永远为正而不需要符号(无符号数)。这有点像在纸上书写数字:当需要考虑符号的时候,数字以加号或减号作为前缀;然而,可以安全地假设为正数时,加号前缀通常省略。有符号数以[补码形式(two’s complement representation)](https://en.wikipedia.org/wiki/Two%27s_complement) 存储。 每一个有符号的变体可以储存包含从 -(2n - 1) 到 2n - 1 - 1 在内的数字,这里 *n* 是变体使用的位数。所以 `i8` 可以储存从 -(27) 到 27 - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从 0 到 2n - 1 的数字,所以 `u8` 可以储存从 0 到 28 - 1 的数字,也就是从 0 到 255。 @@ -65,11 +66,11 @@ error[E0282]: type annotations needed 那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常就很好,数字类型默认是 `i32`:它通常是最快的,甚至在 64 位系统上也是。`isize` 或 `usize` 主要作为某些集合的索引。 -##### 整型溢出 - -比方说有一个 `u8` ,它可以存放从零到 `255` 的值。那么当你将其修改为 `256` 时会发生什么呢?这被称为 “整型溢出”(“integer overflow” ),关于这一行为 Rust 有一些有趣的规则。当在 debug 模式编译时,Rust 检查这类问题并使程序 *panic*,这个术语被 Rust 用来表明程序因错误而退出。第九章会详细介绍 panic。 - -在 release 构建中,Rust 不检测溢出,相反会进行一种被称为二进制补码(*two’s complement wrapping*)的操作。简而言之,`256` 变成 `0`,`257` 变成 `1`,依此类推。依赖整型溢出被认为是一种错误,即便可能出现这种行为。如果你确实需要这种行为,标准库中有一个类型显式提供此功能,`Wrapping`。 +> ##### 整型溢出 +> +> 比方说有一个 `u8` ,它可以存放从零到 `255` 的值。那么当你将其修改为 `256` 时会发生什么呢?这被称为 “整型溢出”(“integer overflow” ),关于这一行为 Rust 有一些有趣的规则。当在 debug 模式编译时,Rust 检查这类问题并使程序 *panic*,这个术语被 Rust 用来表明程序因错误而退出。第九章 [“`panic!` 与不可恢复的错误”][unrecoverable-errors-with-panic] 部分会详细介绍 panic。 +> +> 在 release 构建中,Rust 不检测溢出,相反会进行一种被称为二进制补码包装(*two’s complement wrapping*)的操作。简而言之,`256` 变成 `0`,`257` 变成 `1`,依此类推。依赖整型溢出被认为是一种错误,即便可能出现这种行为。如果你确实需要这种行为,标准库中有一个类型显式提供此功能,[`Wrapping`][wrapping]。 #### 浮点型 @@ -130,7 +131,7 @@ fn main() { } ``` -使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 “控制流”(“Control Flow”)部分将介绍 `if` 表达式在 Rust 中如何工作。 +使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 [“控制流”(“Control Flow”)][control-flow] 部分将介绍 `if` 表达式在 Rust 中如何工作。 #### 字符类型 @@ -146,7 +147,7 @@ fn main() { } ``` -Rust 的 `char` 类型的大小为四个字节(four bytes),并代表了一个 Unicode 标量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。在 Rust 中,拼音字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 `char` 值。Unicode 标量值包含从 `U+0000` 到 `U+D7FF` 和 `U+E000` 到 `U+10FFFF` 在内的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 `char` 并不符合。第八章的 “字符串” 中将详细讨论这个主题。 +Rust 的 `char` 类型的大小为四个字节(four bytes),并代表了一个 Unicode 标量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。在 Rust 中,拼音字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 `char` 值。Unicode 标量值包含从 `U+0000` 到 `U+D7FF` 和 `U+E000` 到 `U+10FFFF` 在内的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 `char` 并不符合。第八章的 [“使用字符串存储 UTF-8 编码的文本”][strings] 中将详细讨论这个主题。 ### 复合类型 @@ -154,7 +155,7 @@ Rust 的 `char` 类型的大小为四个字节(four bytes),并代表了一个 #### 元组类型 -元组是一个将多个其他类型的值组合进一个复合类型的主要方式。 +元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。 我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解: @@ -223,13 +224,19 @@ let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; ``` -数组的类型比较有趣;它看起来像 `[type; number]`。例如: +可以像这样编写数组的类型:在方括号中包含每个元素的类型,后跟分号,再后跟数组元素的数量。 ```rust let a: [i32; 5] = [1, 2, 3, 4, 5]; ``` -首先是方括号;这看起来像创建数组的语法。其中有两部分由分号分割的信息。第一部分是数组中每个元素的类型。因为所有元素都是相同类型的,所以只需列出一次。分号之后,是一个表示数组长度的数字。因为数组是固定长度的,该数字也一直保持不变,即便数组的元素被修改,它也不会增长或缩小。 +这里,`i32` 是每个元素的类型。分号之后,数字 `5` 表明该数组包含五个元素。 + +这样编写数组的类型类似于另一个初始化数组的语法:如果你希望创建一个每个元素都相同的数组,可以在中括号内指定其初始值,后跟分号,再后跟数组的长度,如下所示: + +```rust +let a = [3; 5]; +``` ##### 访问数组元素 @@ -254,7 +261,7 @@ fn main() { 文件名: src/main.rs -```rust,ignore +```rust,ignore,panics fn main() { let a = [1, 2, 3, 4, 5]; let index = 10; @@ -272,11 +279,18 @@ $ cargo run Compiling arrays v0.1.0 (file:///projects/arrays) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running `target/debug/arrays` -thread '
' panicked at 'index out of bounds: the len is 5 but the index is - 10', src/main.rs:6 +thread 'main' panicked at 'index out of bounds: the len is 5 but the index is + 10', src/main.rs:5:19 note: Run with `RUST_BACKTRACE=1` for a backtrace. ``` 编译并没有产生任何错误,不过程序会出现一个 **运行时**(*runtime*)错误并且不会成功退出。当尝试用索引访问一个元素时,Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度,Rust 会 *panic*,这是 Rust 术语,它用于程序因为错误而退出的情况。 这是第一个在实战中遇到的 Rust 安全原则的例子。在很多底层语言中,并没有进行这类检查,这样当提供了一个不正确的索引时,就会访问无效的内存。通过立即退出而不是允许内存访问并继续执行,Rust 让你避开此类错误。第九章会讨论更多 Rust 的错误处理。 + +[comparing-the-guess-to-the-secret-number]: +ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number +[control-flow]: ch03-05-control-flow.html#control-flow +[strings]: ch08-02-strings.html#storing-utf-8-encoded-text-with-strings +[unrecoverable-errors-with-panic]: ch09-01-unrecoverable-errors-with-panic.html +[wrapping]: ../std/num/struct.Wrapping.html \ No newline at end of file diff --git a/src/ch03-03-how-functions-work.md b/src/ch03-03-how-functions-work.md index 987e945..30b64a8 100644 --- a/src/ch03-03-how-functions-work.md +++ b/src/ch03-03-how-functions-work.md @@ -2,7 +2,7 @@ > [ch03-03-how-functions-work.md](https://github.com/rust-lang/book/blob/master/src/ch03-03-how-functions-work.md) >
-> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37 +> commit 669a909a199bc20b913703c6618741d8b6ce1552 函数遍布于 Rust 代码中。你已经见过语言中最重要的函数之一:`main` 函数,它是很多程序的入口点。你也见过 `fn` 关键字,它用来声明新函数。 diff --git a/src/ch03-04-comments.md b/src/ch03-04-comments.md index 84bcd55..08288ed 100644 --- a/src/ch03-04-comments.md +++ b/src/ch03-04-comments.md @@ -2,7 +2,7 @@ > [ch03-04-comments.md](https://github.com/rust-lang/book/blob/master/src/ch03-04-comments.md) >
-> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37 +> commit 75a77762ea2d2ab7fa1e9ef733907ed727c85651 所有程序员都力求使其代码易于理解,不过有时还需要提供额外的解释。在这种情况下,程序员在源码中留下记录,或者 **注释**(*comments*),编译器会忽略它们,不过阅读代码的人可能觉得有用。 @@ -30,7 +30,7 @@ fn main() { } ``` -不过你更经常看到以这种格式使用它们,也就是位于它所解释的代码行的上面一行: +不过你更经常看到的是以这种格式使用它们,也就是位于它所解释的代码行的上面一行: 文件名: src/main.rs @@ -41,4 +41,4 @@ fn main() { } ``` -Rust 还有另一种注释,称为文档注释,我们将在 14 章讨论它。 +Rust 还有另一种注释,称为文档注释,我们将在 14 章的 “将 crate 发布到 Crates.io” 部分讨论它。 diff --git a/src/ch03-05-control-flow.md b/src/ch03-05-control-flow.md index 11e80db..49d7b1c 100644 --- a/src/ch03-05-control-flow.md +++ b/src/ch03-05-control-flow.md @@ -2,7 +2,7 @@ > [ch03-05-control-flow.md](https://github.com/rust-lang/book/blob/master/src/ch03-05-control-flow.md) >
-> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37 +> commit af34ac954a6bd7fc4a8bbcc5c9685e23c5af87da 根据条件是否为真来决定是否执行某些代码,以及根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 `if` 表达式和循环。 @@ -28,7 +28,7 @@ fn main() { -所有的 `if` 表达式都以 `if` 关键字开头,其后跟一个条件。在这个例子中,条件检查变量 `number` 的值是否小于 5。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。`if` 表达式中与条件关联的代码块有时被叫做 *arms*,就像第二章 “比较猜测的数字和秘密数字” 部分中讨论到的 `match` 表达式中的分支一样。 +所有的 `if` 表达式都以 `if` 关键字开头,其后跟一个条件。在这个例子中,条件检查变量 `number` 的值是否小于 5。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。`if` 表达式中与条件关联的代码块有时被叫做 *arms*,就像第二章 [“比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 部分中讨论到的 `match` 表达式中的分支一样。 也可以包含一个可选的 `else` 表达式来提供一个在条件为假时应当执行的代码块,这里我们就这么做了。如果不提供 `else` 表达式并且条件为假时,程序会直接忽略 `if` 代码块并继续执行下面的代码。 @@ -42,7 +42,7 @@ $ cargo run condition was true ``` -尝试改变 `number` 的值使条件为假时看看会发生什么: +尝试改变 `number` 的值使条件为 `false` 时看看会发生什么: ```rust,ignore let number = 7; @@ -79,7 +79,7 @@ error[E0308]: mismatched types --> src/main.rs:4:8 | 4 | if number { - | ^^^^^^ expected bool, found integral variable + | ^^^^^^ expected bool, found integer | = note: expected type `bool` found type `{integer}` @@ -198,7 +198,7 @@ error[E0308]: if and else have incompatible types 6 | | } else { 7 | | "six" 8 | | }; - | |_____^ expected integral variable, found &str + | |_____^ expected integer, found &str | = note: expected type `{integer}` found type `&str` @@ -244,7 +244,7 @@ again! 符号 `^C` 代表你在这按下了ctrl-c。在 `^C` 之后你可能看到也可能看不到 `again!` ,这取决于在接收到终止信号时代码执行到了循环的何处。 -幸运的是,Rust 提供了另一种更可靠的退出循环的方式。可以使用 `break` 关键字来告诉程序何时停止循环。回忆一下在第二章猜猜看游戏的 “猜测正确后退出” 部分使用过它来在用户猜对数字赢得游戏后退出程序。 +幸运的是,Rust 提供了另一种更可靠的退出循环的方式。可以使用 `break` 关键字来告诉程序何时停止循环。回忆一下在第二章猜猜看游戏的 [“猜测正确后退出”][quitting-after-a-correct-guess] 部分使用过它来在用户猜对数字赢得游戏后退出程序。 #### 从循环返回 @@ -266,6 +266,8 @@ fn main() { } ``` +在循环之前,我们声明了一个名为 `counter` 的变量并初始化为 `0`。接着声明了一个名为 `result` 来存放循环的返回值。在循环的每一次迭代中,我们将 `counter` 变量加 `1`,接着检查计数是否等于 `10`。当相等时,使用 `break` 关键字返回值 `counter * 2`。循环之后,我们通过分号结束赋值给 `result` 的语句。最后打印出 `result` 的值,也就是 20。 + #### `while` 条件循环 在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用 `break` 停止循环。这个循环类型可以通过组合 `loop`、`if`、`else` 和 `break` 来实现;如果你喜欢的话,现在就可以在程序中试试。 @@ -376,4 +378,9 @@ fn main() { * 生成 n 阶斐波那契数列。 * 打印圣诞颂歌 “The Twelve Days of Christmas” 的歌词,并利用歌曲中的重复部分(编写循环)。 -当你准备好继续的时候,让我们讨论一个其他语言中 *并不* 常见的概念:所有权(ownership)。 +当你准备好继续的时候,让我们讨论一个其他语言中 **并不** 常见的概念:所有权(ownership)。 + +[comparing-the-guess-to-the-secret-number]: +ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number +[quitting-after-a-correct-guess]: +ch02-00-guessing-game-tutorial.html#quitting-after-a-correct-guess \ No newline at end of file diff --git a/src/ch04-01-what-is-ownership.md b/src/ch04-01-what-is-ownership.md index 205920c..29d0159 100644 --- a/src/ch04-01-what-is-ownership.md +++ b/src/ch04-01-what-is-ownership.md @@ -1,8 +1,8 @@ -## 什么是所有权 +## 什么是所有权? > [ch04-01-what-is-ownership.md](https://github.com/rust-lang/book/blob/master/src/ch04-01-what-is-ownership.md) >
-> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37 +> commit e81710c276b3839e8ec54d5f12aec4f9de88924b Rust 的核心功能(之一)是 **所有权**(*ownership*)。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。 @@ -18,9 +18,7 @@ Rust 的核心功能(之一)是 **所有权**(*ownership*)。虽然该 > > 栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 **后进先出**(*last in, first out*)。想象一下一叠盘子:当增加更多盘子时,把它们放在盘子堆的顶部,当需要盘子时,也从顶部拿走。不能从中间也不能从底部增加或拿走盘子!增加数据叫做 **进栈**(*pushing onto the stack*),而移出数据叫做 **出栈**(*popping off the stack*)。 > -> 栈的操作是十分快速的,这主要是得益于它存取数据的方式:因为数据存取的位置总是在栈顶而不需要寻找一个位置存放或读取数据。另一个让操作栈快速的属性是,栈中的所有数据都必须占用已知且固定的大小。 -> -> 在编译时大小未知或大小可能变化的数据,要改为存储在堆上。堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 **指针**(*pointer*)。这个过程称作 **在堆上分配内存**(*allocating on the heap*),有时简称为 “分配”(allocating)。将数据推入栈中并不被认为是分配。因为指针的大小是已知并且固定的,你可以将指针存储在栈上,不过当需要实际数据时,必须访问指针。 +> 栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 **指针**(*pointer*)。这个过程称作 **在堆上分配内存**(*allocating on the heap*),有时简称为 “分配”(allocating)。将数据推入栈中并不被认为是分配。因为指针的大小是已知并且固定的,你可以将指针存储在栈上,不过当需要实际数据时,必须访问指针。 > > 想象一下去餐馆就座吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。 > @@ -69,7 +67,7 @@ let s = "hello"; ### `String` 类型 -为了演示所有权的规则,我们需要一个比第三章 “数据类型” 中讲到的都要复杂的数据类型。前面介绍的类型都是存储在栈上的并且当离开作用域时被移出栈,不过我们需要寻找一个存储在堆上的数据来探索 Rust 是如何知道该在何时清理数据的。 +为了演示所有权的规则,我们需要一个比第三章 [“数据类型”][data-types] 中讲到的都要复杂的数据类型。前面介绍的类型都是存储在栈上的并且当离开作用域时被移出栈,不过我们需要寻找一个存储在堆上的数据来探索 Rust 是如何知道该在何时清理数据的。 这里使用 `String` 作为例子,并专注于 `String` 与所有权相关的部分。这些方面也同样适用于标准库提供的或你自己创建的其他复杂数据类型。在第八章会更深入地讲解 `String`。 @@ -79,7 +77,7 @@ let s = "hello"; let s = String::from("hello"); ``` -这两个冒号(`::`)是运算符,允许将特定的 `from` 函数置于 `String` 类型的命名空间(namespace)下,而不需要使用类似 `string_from` 这样的名字。在第五章的 “方法语法”(“Method Syntax”)部分会着重讲解这个语法而且在第七章的 “模块定义” 中会讲到模块的命名空间。 +这两个冒号(`::`)是运算符,允许将特定的 `from` 函数置于 `String` 类型的命名空间(namespace)下,而不需要使用类似 `string_from` 这样的名字。在第五章的 [“方法语法”(“Method Syntax”)][method-syntax] 部分会着重讲解这个语法而且在第七章的 [“路径用于引用模块树中的项”][paths-module-tree] 中会讲到模块的命名空间。 **可以** 修改此类字符串 : @@ -234,7 +232,7 @@ println!("x = {}, y = {}", x, y); 原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 `y` 后使 `x` 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 `clone` 并不会与通常的浅拷贝有什么不同,我们可以不用管它。 -Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上(第十章详细讲解 trait)。如果一个类型拥有 `Copy` trait,一个旧的变量在将其赋值给其他变量后仍然可用。Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Copy` trait。如果我们对其值离开作用域时需要特殊处理的类型使用 `Copy` 注解,将会出现一个编译时错误。要学习如何为你的类型增加 `Copy` 注解,请阅读附录 C 中的 “可派生的 trait”。 +Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上(第十章详细讲解 trait)。如果一个类型拥有 `Copy` trait,一个旧的变量在将其赋值给其他变量后仍然可用。Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Copy` trait。如果我们对其值离开作用域时需要特殊处理的类型使用 `Copy` 注解,将会出现一个编译时错误。要学习如何为你的类型增加 `Copy` 注解,请阅读附录 C 中的 [“可派生的 trait”][derivable-traits]。 那么什么类型是 `Copy` 的呢?可以查看给定类型的文档来确认,不过作为一个通用的规则,任何简单标量值的组合可以是 `Copy` 的,不需要分配内存或某种形式资源的类型是 `Copy` 的。如下是一些 `Copy` 的类型: @@ -341,3 +339,8 @@ fn calculate_length(s: String) -> (String, usize) { 示例 4-5: 返回参数的所有权 但是这未免有些形式主义,而且这种场景应该很常见。幸运的是,Rust 对此提供了一个功能,叫做 **引用**(*references*)。 + +[data-types]: ch03-02-data-types.html#data-types +[derivable-traits]: appendix-03-derivable-traits.html +[method-syntax]: ch05-03-method-syntax.html#method-syntax +[paths-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html \ No newline at end of file diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index f79947c..321a81b 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -2,7 +2,7 @@ > [ch04-02-references-and-borrowing.md](https://github.com/rust-lang/book/blob/master/src/ch04-02-references-and-borrowing.md) >
-> commit 6c0d83499c8e77e06a71d28c5e1adccec278d4f3 +> commit 4f19894e592cd24ac1476f1310dcf437ae83d4ba 示例 4-5 中的元组代码有这样一个问题:我们必须将 `String` 返回给调用函数,以便在调用 `calculate_length` 后仍能使用 `String`,因为 `String` 被移动到了 `calculate_length` 内。 @@ -182,10 +182,10 @@ println!("{}, {}, and {}", r1, r2, r3); error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable --> src/main.rs:6:14 | -4 | let r1 = &s; // 没问题 +4 | let r1 = &s; // no problem | -- immutable borrow occurs here -5 | let r2 = &s; // 没问题 -6 | let r3 = &mut s; // 大问题 +5 | let r2 = &s; // no problem +6 | let r3 = &mut s; // BIG PROBLEM | ^^^^^^ mutable borrow occurs here 7 | 8 | println!("{}, {}, and {}", r1, r2, r3); @@ -196,10 +196,6 @@ error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immuta 注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。例如,因为最后一次使用不可变引用在声明可变引用之前,所以如下代码是可以编译的: - - ```rust,edition2018,ignore let mut s = String::from("hello"); @@ -253,13 +249,14 @@ error[E0106]: missing lifetime specifier 错误信息引用了一个我们还未介绍的功能:生命周期(lifetimes)。第十章会详细介绍生命周期。不过,如果你不理会生命周期部分,错误信息中确实包含了为什么这段代码有问题的关键信息: ```text -this function's return type contains a borrowed value, but there is no value -for it to be borrowed from. +this function's return type contains a borrowed value, but there is no value for it to be borrowed from. ``` 让我们仔细看看我们的 `dangle` 代码的每一步到底发生了什么: -```rust,ignore +文件名: src/main.rs + +```rust,ignore,does_not_compile fn dangle() -> &String { // dangle 返回一个字符串的引用 let s = String::from("hello"); // s 是一个新字符串 @@ -288,6 +285,6 @@ fn no_dangle() -> String { 让我们概括一下之前对引用的讨论: * 在任意给定时间,**要么** 只能有一个可变引用,**要么** 只能有多个不可变引用。 -* 引用必须总是有效。 +* 引用必须总是有效的。 接下来,我们来看看另一种不同类型的引用:slice。 diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index 8373159..0f66a59 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -2,7 +2,7 @@ > [ch04-03-slices.md](https://github.com/rust-lang/book/blob/master/src/ch04-03-slices.md) >
-> commit 6678e8221a9e7d79bffe47749bf63978b071c709 +> commit 9fcebe6e1b0b5e842285015dbf093f97cd5b3803 另一个没有所有权的数据类型是 *slice*。slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。 @@ -115,18 +115,9 @@ let hello = &s[0..5]; let world = &s[6..11]; ``` -这类似于引用整个 `String` 不过带有额外的 `[0..5]` 部分。它不是对整个 `String` 的引用,而是对部分 `String` 的引用。`start..end` 语法代表一个以 `start` 开头并一直持续到但不包含 `end` 的 range。如果需要包含 `end`,可以使用 `..=` 而不是 `..`: +这类似于引用整个 `String` 不过带有额外的 `[0..5]` 部分。它不是对整个 `String` 的引用,而是对部分 `String` 的引用。 -```rust -let s = String::from("hello world"); - -let hello = &s[0..=4]; -let world = &s[6..=10]; -``` - -`=` 意味着包含最后的数字,这有助于你记住 `..` 与 `..=` 的区别 - -可以使用一个由中括号中的 `[starting_index..ending_index]` 指定的 range 创建一个 slice,其中 `starting_index` 是 slice 的第一个位置,`ending_index` 则是 slice 最后一个位置的后一个值。在其内部,slice 的数据结构存储了 slice 的开始位置和长度,长度对应于 `ending_index` 减去 `starting_index` 的值。所以对于 `let world = &s[6..11];` 的情况,`world` 将是一个包含指向 `s` 第 7 个字节的指针和长度值 5 的 slice。 +可以使用一个由中括号中的 `[starting_index..ending_index]` 指定的 range 创建一个 slice,其中 `starting_index` 是 slice 的第一个位置,`ending_index` 则是 slice 最后一个位置的后一个值。在其内部,slice 的数据结构存储了 slice 的开始位置和长度,长度对应于 `ending_index` 减去 `starting_index` 的值。所以对于 `let world = &s[6..11];` 的情况,`world` 将是一个包含指向 `s` 第 7 个字节(从 1 开始)的指针和长度值 5 的 slice。 图 4-6 展示了一个图例。 @@ -143,7 +134,7 @@ let slice = &s[0..2]; let slice = &s[..2]; ``` -由此类推,如果 slice 包含 `String` 的最后一个字节,也可以舍弃尾部的数字。这意味着如下也是相同的: +依此类推,如果 slice 包含 `String` 的最后一个字节,也可以舍弃尾部的数字。这意味着如下也是相同的: ```rust let s = String::from("hello"); @@ -165,7 +156,7 @@ let slice = &s[0..len]; let slice = &s[..]; ``` -> 注意:字符串 slice range 的索引必须位于有效的 UTF-8 字符边界内,如果尝试从一个多字节字符的中间位置创建字符串 slice,则程序将会因错误而退出。出于介绍字符串 slice 的目的,本部分假设只使用 ASCII 字符集;第八章的 “使用字符串存储 UTF-8 编码的文本” 部分会更加全面的讨论 UTF-8 处理问题。 +> 注意:字符串 slice range 的索引必须位于有效的 UTF-8 字符边界内,如果尝试从一个多字节字符的中间位置创建字符串 slice,则程序将会因错误而退出。出于介绍字符串 slice 的目的,本部分假设只使用 ASCII 字符集;第八章的 [“使用字符串存储 UTF-8 编码的文本”][strings] 部分会更加全面的讨论 UTF-8 处理问题。 在记住所有这些知识后,让我们重写 `first_word` 来返回一个 slice。“字符串 slice” 的类型声明写作 `&str`: @@ -205,7 +196,7 @@ fn main() { let word = first_word(&s); - s.clear(); // error! + s.clear(); // 错误! println!("the first word is: {}", word); } @@ -215,16 +206,16 @@ fn main() { ```text error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable - --> src/main.rs:10:5 + --> src/main.rs:18:5 | -8 | let word = first_word(&s); +16 | let word = first_word(&s); | -- immutable borrow occurs here -9 | -10 | s.clear(); // error! +17 | +18 | s.clear(); // error! | ^^^^^^^^^ mutable borrow occurs here -11 | -12 | println!("the first word is: {}", word); - | ---- borrow later used here +19 | +20 | println!("the first word is: {}", word); + | ---- immutable borrow later used here ``` 回忆一下借用规则,当拥有某值的不可变引用时,就不能再获取一个可变引用。因为 `clear` 需要清空 `String`,它尝试获取一个可变引用。Rust不允许这样做,因而编译失败。Rust 不仅使得我们的 API 简单易用,也在编译时就消除了一整类的错误! @@ -311,3 +302,5 @@ let slice = &a[1..3]; 所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。 所有权系统影响了 Rust 中很多其他部分的工作方式,所以我们还会继续讲到这些概念,这将贯穿本书的余下内容。让我们开始第五章,来看看如何将多份数据组合进一个 `struct` 中。 + +[strings]: ch08-02-strings.html#storing-utf-8-encoded-text-with-strings diff --git a/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md b/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md index 69d8e48..c045739 100644 --- a/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md +++ b/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md @@ -1,4 +1,4 @@ -## 路径来引用模块树中的项 +## 路径用于引用模块树中的项 > [ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md](https://github.com/rust-lang/book/blob/master/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md) >