Refining Ch04.

This commit is contained in:
rust-lang.xfoss.com 2023-12-12 17:38:18 +08:00
parent f6694b1b61
commit 584cf03a38

View File

@ -34,66 +34,80 @@
**Ownership Rules**
首先,来看看这些所有权规则。在完成后面用于演示这些规则的示例时,请牢记这些规则:
首先,我们来看看这些所有权规则。在我们举例说明他们时,请牢记这些规则:
- Rust 中的每个值,都有个 *所有者owner*
- Rust 中的每个值,都有一个名为 *所有者owner* 的变量;
- 同一时间,只能有一个所有者;
- 在其所有者超出作用域scope 时,该值就被丢弃。
- 当所有者超出作用域时,该值将被丢弃。
## 变量作用域
**Variable Scope**
既然已经学了 Rust 基础语法,接下来就不会在示例中,包含整个的 `fn main() {` 代码了,那么若跟随这些示例,就要确保把接下来的这些示例,自己手动放在 `main` 函数里头。这样的结果就是,这些示例会比较精炼一点,着重于具体细节而不是那些样板代码。
作为所有权的首个示例,这里将考察一下一些变量的 *作用域scope*。作用域是指某个项目在程序中的有效范围。以下面这个变量来说:
既然我们已经掌握了 Rust 的基本语法,我们就不会在示例中,包含所有 `fn main() {` 代码,所以如果咱们正在学习,请务必手动将下面的示例,放在某个 `main` 函数中。如此,我们的示例将更加简洁,让我们专注于具体细节,而不是样板代码了。
作为所有权的首个例子,我们来看看,一些变量的作用域。作用域是指某个项目在程序中,有效的范围。以下面的变量为例:
```rust
let s = "hello";
```
这里的变量 `s` 指向一个字符串字面值,其中的字符串的值,则是被硬编码到这个程序的文本。自变量被声明处,到当前 *作用域* 结束处,变量都是有效的。下面清单 4-1 给出了一个带有对变量 `s` 在何处有效,进行注解注释的程序:
变量 `s` 指向某个字符串字面值,其中该字符串的值,被硬编码到我们程序的文本中。这个变量从其被声明时开始,直到当前作用域结束,都是有效的。下面清单 4-1 给出了一个带有说明这个变量 `s` 的于何处有效注释的程序。
```rust
{ // 变量 s 在这里是无效的,他还没被声明出来
let s = "hello"; // s 自此往下都是有效的
// 对变量 s 执行一些操作
} // 此时该作用域结束,而变量 s 不再有效
} // 此时该作用域结束,而变量 s 不再有效
```
*清单 4-1变量与其间有效的作用域a variable and the scope in which it is valid*
换句话说,这里有两个重点:
- 当变量 `s` 一旦来到作用域他就有效了when `s` comes *into scope*, it is valid
- 他会保持有效,直到 *超出作用域*it remains valid until it goes *out of scope*
*清单 4-1一个变量及其有效的作用域*
到这里,作用域和变量何时有效二者之间的关系,与其他语言中的此类关系类似。现在就要通过引入 `String` 类型在此理解之上建构出所有权的理解now we'll build on top of this understanding by introducing the `String` type。
换句话说,这里有两个重要的时间点:
- 当 `s`*入* 作用域时,他便有效了;
- 在超 *出* 作用域之前,他会一直有效。
此时,作用域和变量何时有效之间的关系,与其他编程语言中的类似。现在我们将通过引入 `String` 类型,在这种理解的基础上构建。
## `String` 类型
为了对所有权的那些规则进行演示,就需要比前面第 3 章的 ["数据类型"](Ch03_Common_Programming_Concepts.md#数据类型) 小节中讲到那些类型,更为复杂一些的数据类型。前面讲到的那些类型,都是已知大小、可存储在栈上的,且在他们的作用域结束时会被弹出栈,在代码另一部分需要在不同作用域中用到同一值时,这些类型还可被快速而简单地复制,而构造出新的、独立实例。不过这里要审视的是存储在内存堆上的数据,进而探讨 Rust 是如何知晓,何时要清理这些内存堆上的数据,那么 `String` 类型就是极佳的示例了。
**The `String` Type**
这里将着重于 `String` 类型与所有权有关的部分。这些方面同样适用于其他的、不论是由标准库还是自己创建的复合数据类型complex data types。在 [第 8 章](Ch08_Common_Collections.md#何为-string) 将深入讲解 `String` 类型。
前面咱们已经见到了一些字符串字面值其中有个硬编码到程序里的字符串值。字符串字面值很方便但对于那些打算使用文本的全部情形他们却并不适合。一个原因是字符串字面值为不可变的。另一个原因则是在编写代码时并非每个字符串的值都是已知的比如假设要获取用户输入并存储下来呢对于这样的情形Rust 有着第二种字符串类型,即 `String`。这种类型对分配到内存堆上的数据加以管理,并因此而具备了存储在编译时数量未知文本的能力。使用 `String` 类型的 `from` 函数,就可以从字符串字面值,创建出一个 `String` 类型的值来,如下所示:
为了说明所有权规则,我们需要一种比第 3 章 [数据类型](../programming_concepts/data_types.md) 小节中,介绍的数据类型更复杂的一种数据类型。前面介绍的类型大小已知,可被存储在栈中,并在其作用域结束时,从栈中弹出,如果代码的另一部分,需要在不同的作用域中使用同一个值,他们就可以快速、简便地复制,以创建一个新的、独立的实例。但我们打算看看存储在堆上的数据,并探讨 Rust 如何知道,何时清理这些数据,而 `String` 类型就是个很好的例子。
我们将重点关注 `String` 中,与所有权相关的部分。这些方面也适用于其他复杂的数据类型,无论它们是由标准库提供,还是由咱们自己创建。我们将在 [第 8 章](../common_collections/strings.md) 中,更深入地讨论 `String`
我们已经见过字符串的字面值其中某个字符串值被硬编码到咱们的程序中。字符串字面值很方便但他们并不适合我们打算使用文本的所有情况。其中一个原因是他们是不可变的。另一个原因是在我们编写代码时并非每个字符串值都是已知的例如如果我们打算获取用户输入并将其存储起来该怎么办针对这些情况Rust 有着第二种字符串类型 -- `String`。该类型管理堆上分配的数据,因此可以存储编译时咱们未知数量的文本。咱们可以使用 `from` 函数,从某个字符串字面值创建出一个 `String`,如下所示:
```rust
let s = String::from("hello");
// 变量 s 的类型为String, 而此前字面值中的变量 s 的类型为:&str
```
其中的双冒号(`::`)运算符,实现了将这个特定 `from` 函数,置于 `String` 类型的命名空间之下,而无需使用类似于 `string_from` 这种名字了。在第 5 章的 [方法语法](Ch05_Using_Structs_to_Structure_Related_Data.md#方法语法) 小节,并在第 7 章的 [对模组树中的某个项目进行引用的路径](Ch07_Managing_Growing_Projects_with_Packages_Crates_and_Modules.md#用于引用目录树中项目的路径) 小节,对模组命名空间的介绍中,将对这种语法进行更多讲解。
这种字符串,*能* 被改变:
其中双冒号 `::` 操作符,允许我们在 `String` 类型下,命名这个特殊的 `from` 函数,而不是使用诸如 `string_from` 的某种名字。我们将在第 5 章的 [方法语法](../structs/method_syntax.md) 小节,以及第 7 章的 [用于引用模块树中某个项目的路径](../packages_crates_and_modules/paths.md) 小节,讨论模块命名空间时,进一步讨论这种语法。
这种字符串,*可* 被改变:
```rust
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() 方法把字面值追加到某个字符串
s.push_str(", world!"); // push_str() 方法会追加一个字面值,到某个 String
println! ("{}", s); // 这将打印出 `hello, world!`
```