|
|
@ -2,13 +2,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
> [ch03-01-variables-and-mutability.md](https://github.com/rust-lang/book/blob/main/src/ch03-01-variables-and-mutability.md)
|
|
|
|
> [ch03-01-variables-and-mutability.md](https://github.com/rust-lang/book/blob/main/src/ch03-01-variables-and-mutability.md)
|
|
|
|
> <br>
|
|
|
|
> <br>
|
|
|
|
> commit 05d9c4c2312a6542f792492d17a62f79ad6dfd7b
|
|
|
|
> commit d281b7b062e6dbfbcf47f8381073f7fce9e5cd4e
|
|
|
|
|
|
|
|
|
|
|
|
正如第二章中[“使用变量储存值”][storing-values-with-variables]<!-- ignore --> 部分提到的那样,变量默认是不可改变的(immutable)。这是 Rust 提供给你的众多优势之一,让你得以充分利用 Rust 提供的安全性和简单并发性来编写代码。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 为何及如何鼓励你利用不可变性,以及何时你会选择不使用不可变性。
|
|
|
|
正如第二章中[“使用变量储存值”][storing-values-with-variables]<!-- ignore --> 部分提到的那样,变量默认是不可改变的(immutable)。这是 Rust 提供给你的众多优势之一,让你得以充分利用 Rust 提供的安全性和简单并发性来编写代码。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 为何及如何鼓励你利用不可变性,以及何时你会选择不使用不可变性。
|
|
|
|
|
|
|
|
|
|
|
|
当变量不可变时,一旦值被绑定一个名称上,你就不能改变这个值。为了对此进行说明,使用 `cargo new variables` 命令在 *projects* 目录生成一个叫做 *variables* 的新项目。
|
|
|
|
当变量不可变时,一旦值被绑定一个名称上,你就不能改变这个值。为了对此进行说明,使用 `cargo new variables` 命令在 *projects* 目录生成一个叫做 *variables* 的新项目。
|
|
|
|
|
|
|
|
|
|
|
|
接着,在新建的 *variables* 目录,打开 *src/main.rs* 并将代码替换为如下代码,这些代码还不能编译:
|
|
|
|
接着,在新建的 *variables* 目录,打开 *src/main.rs* 并将代码替换为如下代码,这些代码还不能编译,我们会首次检查到不可变错误(immutability error)。
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
@ -52,9 +52,9 @@ To learn more, run the command again with --verbose.
|
|
|
|
|
|
|
|
|
|
|
|
在尝试改变预设为不可变的值时,产生编译时错误是很重要的,因为这种情况可能导致 bug。如果一部分代码假设一个值永远也不会改变,而另一部分代码改变了这个值,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 的起因难以跟踪,尤其是第二部分代码只是 **有时** 会改变值。
|
|
|
|
在尝试改变预设为不可变的值时,产生编译时错误是很重要的,因为这种情况可能导致 bug。如果一部分代码假设一个值永远也不会改变,而另一部分代码改变了这个值,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 的起因难以跟踪,尤其是第二部分代码只是 **有时** 会改变值。
|
|
|
|
|
|
|
|
|
|
|
|
Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要追踪一个值如何和在哪可能会被改变,从而使得代码易于推导。
|
|
|
|
Rust 编译器保证,如果声明一个值不会变,它就真的不会变,所以你不必自己跟踪它。这意味着你的代码更易于推导。
|
|
|
|
|
|
|
|
|
|
|
|
不过可变性也是非常有用的。变量只是默认不可变;正如在第二章所做的那样,你可以在变量名之前加 `mut` 来使其可变。除了允许改变值之外,`mut` 向读者表明了其他代码将会改变这个变量值的意图。
|
|
|
|
不过可变性也是非常有用的,可以用来更方便地编写代码。变量只是默认不可变;正如在第二章所做的那样,你可以在变量名之前加 `mut` 来使其可变。`mut` 也向读者表明了其他代码将会改变这个变量值的意图。
|
|
|
|
|
|
|
|
|
|
|
|
例如,让我们将 *src/main.rs* 修改为如下代码:
|
|
|
|
例如,让我们将 *src/main.rs* 修改为如下代码:
|
|
|
|
|
|
|
|
|
|
|
@ -80,13 +80,11 @@ The value of x is: 5
|
|
|
|
The value of x is: 6
|
|
|
|
The value of x is: 6
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。在一些情况下,你会想用可变变量,因为与只用不可变变量相比,它会让代码更容易编写。
|
|
|
|
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。除了防止出现 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解,为可读性而牺牲性能或许是值得的。
|
|
|
|
|
|
|
|
|
|
|
|
除了防止出现 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解,为可读性而牺牲性能或许是值得的。
|
|
|
|
### 常量
|
|
|
|
|
|
|
|
|
|
|
|
### 变量和常量的区别
|
|
|
|
类似于不可变变量,*常量(constants)* 是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
|
|
|
|
|
|
|
|
|
|
|
|
不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:**常量**(*constants*)。类似于不可变变量,常量是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
首先,不允许对常量使用 `mut`。常量不光默认不能变,它总是不能变。
|
|
|
|
首先,不允许对常量使用 `mut`。常量不光默认不能变,它总是不能变。
|
|
|
|
|
|
|
|
|
|
|
@ -96,13 +94,13 @@ The value of x is: 6
|
|
|
|
|
|
|
|
|
|
|
|
最后一个区别是,常量只能被设置为常量表达式,而不可以是其他任何只能在运行时计算出的值。
|
|
|
|
最后一个区别是,常量只能被设置为常量表达式,而不可以是其他任何只能在运行时计算出的值。
|
|
|
|
|
|
|
|
|
|
|
|
下面是一个声明常量的例子,常量的名称是 `THREE_HOURS_IN_SECONDS`,它的值被设置为 60(一分钟内的秒数)乘以 60(一小时内的分钟数)再乘以 3(我们在这个程序中要计算的小时数)的结果:
|
|
|
|
下面是一个声明常量的例子:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
|
|
|
|
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Rust 对常量的命名约定是在单词之间使用全大写加下划线。编译器能够在编译时计算一组有限的操作,这使我们可以选择以更容易理解和验证的方式写出此值,而不是将此常量设置为值10,800。有关声明常量时可以使用哪些操作的详细信息,请参阅 [Rust Reference 的常量求值部分][const-eval]。
|
|
|
|
常量的名称是 `THREE_HOURS_IN_SECONDS`,它的值被设置为 60(一分钟内的秒数)乘以 60(一小时内的分钟数)再乘以 3(我们在这个程序中要计算的小时数)的结果。Rust 对常量的命名约定是在单词之间使用全大写加下划线。编译器能够在编译时计算一组有限的操作,这使我们可以选择以更容易理解和验证的方式写出此值,而不是将此常量设置为值10,800。有关声明常量时可以使用哪些操作的详细信息,请参阅 [Rust Reference 的常量求值部分][const-eval]。
|
|
|
|
|
|
|
|
|
|
|
|
在声明它的作用域之中,常量在整个程序生命周期中都有效,此属性使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
|
|
|
|
在声明它的作用域之中,常量在整个程序生命周期中都有效,此属性使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
|
|
|
|
|
|
|
|
|
|
|
@ -110,7 +108,7 @@ Rust 对常量的命名约定是在单词之间使用全大写加下划线。编
|
|
|
|
|
|
|
|
|
|
|
|
### 隐藏(Shadowing)
|
|
|
|
### 隐藏(Shadowing)
|
|
|
|
|
|
|
|
|
|
|
|
正如在第二章猜猜看游戏的 [“比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 中所讲,我们可以定义一个与之前变量同名的新变量。Rustacean 们称之为第一个变量被第二个 **隐藏** 了,这意味着程序使用这个变量时会看到第二个值。可以用相同变量名称来隐藏一个变量,以及重复使用 `let` 关键字来多次隐藏,如下所示:
|
|
|
|
正如在[第二章][comparing-the-guess-to-the-secret-number]猜猜看游戏中所讲,我们可以定义一个与之前变量同名的新变量。Rustacean 们称之为第一个变量被第二个 **隐藏** 了,这意味着程序使用这个变量时会看到第二个值。可以用相同变量名称来隐藏一个变量,以及重复使用 `let` 关键字来多次隐藏,如下所示:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
@ -142,14 +140,14 @@ The value of x is: 6
|
|
|
|
|
|
|
|
|
|
|
|
隐藏与将变量标记为 `mut` 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 `let` 关键字,就会导致编译时错误。通过使用 `let`,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。
|
|
|
|
隐藏与将变量标记为 `mut` 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 `let` 关键字,就会导致编译时错误。通过使用 `let`,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。
|
|
|
|
|
|
|
|
|
|
|
|
`mut` 与隐藏的另一个区别是,当再次使用 `let` 时,实际上创建了一个新变量,我们可以改变值的类型,但复用这个名字。例如,假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格,然而我们真正需要的是将输入存储成数字(多少个空格):
|
|
|
|
`mut` 与隐藏的另一个区别是,当再次使用 `let` 时,实际上创建了一个新变量,我们可以改变值的类型,但复用这个名字。例如,假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格,接下来我们想将输入存储成数字(多少个空格):
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
let spaces = " ";
|
|
|
|
let spaces = " ";
|
|
|
|
let spaces = spaces.len();
|
|
|
|
let spaces = spaces.len();
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这里允许第一个 `spaces` 变量是字符串类型,而第二个 `spaces` 变量,它是一个恰巧与第一个变量同名的崭新变量,是数字类型。隐藏使我们不必使用不同的名字,如 `spaces_str` 和 `spaces_num`;相反,我们可以复用 `spaces` 这个更简单的名字。然而,如果尝试使用 `mut`,将会得到一个编译时错误,如下所示:
|
|
|
|
第一个 `spaces` 变量是字符串类型,第二个 `spaces` 变量是数字类型。隐藏使我们不必使用不同的名字,如 `spaces_str` 和 `spaces_num`;相反,我们可以复用 `spaces` 这个更简单的名字。然而,如果尝试使用 `mut`,将会得到一个编译时错误,如下所示:
|
|
|
|
|
|
|
|
|
|
|
|
```rust,ignore,does_not_compile
|
|
|
|
```rust,ignore,does_not_compile
|
|
|
|
let mut spaces = " ";
|
|
|
|
let mut spaces = " ";
|
|
|
|