You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trpl-zh-cn/src/ch03-01-variables-and-mutab...

151 lines
7.4 KiB

8 years ago
## 变量和可变性
> [ch03-01-variables-and-mutability.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch03-01-variables-and-mutability.md)
8 years ago
> <br>
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8
8 years ago
8 years ago
第二章中提到过,变量默认是**不可变***immutable*)的。这是利用 Rust 安全和简单并发的优势编写代码一大助力。不过,变量仍然有可变的选项。让我们探讨一下,拥抱不可变性的原因及方法,以及何时你不想拥抱。
8 years ago
8 years ago
当变量不可变时,意味着一旦值被绑定上一个名称,你就不能改变这个值。作为说明,通过`cargo new --bin variables`在 *projects* 目录生成一个叫做 *variables* 的新项目。
8 years ago
接着,在新建的 *variables* 目录,打开 *src/main.rs* 并替换其代码为如下:
<span class="filename">Filename: src/main.rs</span>
```rust,ignore
fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
```
保存并使用`cargo run`运行程序。应该会看到一个错误信息,如下输出所示:
```
8 years ago
$ cargo run
Compiling variables v0.0.1 (file:///projects/variables)
error[E0384]: re-assignment of immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| - first assignment to `x`
3 | println!("The value of x is: {}", x);
4 | x = 6;
| ^^^^^ re-assignment of immutable variable
```
8 years ago
这个例子展示了编译器如何帮助你找出程序中的错误。即便编译错误令人沮丧,那也不过是说程序不能安全的完成你想让它完成的工作;而**不能**说明你是不是一个好程序员!有经验的 Rustacean 们一样会遇到编译错误。这些错误给出的原因是`对不可变变量重新赋值``re-assignment of immutable variable`),因为我们尝试对不可变变量`x`赋第二个值。
8 years ago
8 years ago
尝试去改变预设为不可变的值,产生编译错误是很重要的,因为这种情况可能导致 bug如果代码的一部分假设一个值永远也不会改变而另一部分代码改变了它第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是**有时**改变其值。
8 years ago
8 years ago
Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要厘清如何以及哪里可能会被改变,从而使得代码易于推导。
8 years ago
8 years ago
不过可变性也是非常有用的。变量只是默认不可变;可以通过在变量名之前加 `mut` 来使其可变。它向读者表明了其他代码将会改变这个变量的意图。
8 years ago
例如,改变 *src/main.rs* 并替换其代码为如下:
<span class="filename">Filename: src/main.rs</span>
```rust
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
```
当运行这个程序,出现如下:
```
8 years ago
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
```
8 years ago
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。在一些情况下,你会想要一个变量可变,因为相对不可变的风格更容易写。
8 years ago
8 years ago
除了避免 bug 外,还有多处需要权衡取舍。例如,使用大型数据结构时,适当地使变量可变,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的风格编程,可能会使代码更易理解,为可读性而遭受性能惩罚或许值得。
8 years ago
### 变量和常量的区别
8 years ago
不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:**常量***constants*)。类似于不可变变量,常量也是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
8 years ago
首先,不允许对常量使用 `mut`:常量不光默认不能变,它总是不能变。
8 years ago
声明常量使用 `const` 关键字而不是 `let`,而且*必须*注明值的类型。在下一部分,“数据类型”,涉及到类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。
8 years ago
常量可以在任何作用域声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。
8 years ago
最后一个区别是常量只能用于常量表达式,而不能作为函数调用的结果,或任何其他只在运行时计算的值。
8 years ago
这是一个常量声明的例子,它的名称是 `MAX_POINTS`,值是 100,000。常量使用下划线分隔的大写字母命名
8 years ago
```rust
const MAX_POINTS: u32 = 100_000;
```
8 years ago
常量在整个程序生命周期中都有效,位于它声明的作用域之中。这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
8 years ago
8 years ago
将作用于整个程序的值,由硬编码改为常量(并编写文档),对后来的维护者了解值的意义很用帮助。它也能将硬编码的值汇总一处,为将来可能的修改提供方便。
8 years ago
8 years ago
### 遮盖Shadowing
8 years ago
8 years ago
如第二章“猜猜看游戏”所讲的,我们可以定义一个与之前变量重名的新变量,而新变量会**遮盖**之前的变量。Rustacean 称之为“第一个变量被第二个**遮盖**了”,这意味着使用这个变量时会看第二个值。可以用相同变量名称来遮盖它自己,以及重复使用 `let` 关键字来多次遮盖,如下所示:
8 years ago
<span class="filename">Filename: src/main.rs</span>
```rust
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("The value of x is: {}", x);
}
```
8 years ago
这个程序首先将 `x` 绑定到值 `5` 上。接着通过 `let x =` 遮盖 `x`,获取原始值并加 `1` 这样 `x` 的值就变成 `6` 了。第三个 `let` 语句也覆盖了 `x`,获取之前的值并乘以 `2``x` 最终的值是 `12`。运行这个程序,它会有如下输出:
8 years ago
```
8 years ago
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Running `target/debug/variables`
The value of x is: 12
```
8 years ago
这与将变量声明为 `mut` 是有区别的。因为除非再次使用 `let` 关键字,不小心尝试对变量重新赋值会导致编译时错误。我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。
8 years ago
8 years ago
`mut` 与遮盖的另一个区别是,当再次使用 `let` 时,实际上创建了一个新变量,我们可以改变值的类型。例如,假设程序请求用户输入空格来提供文本间隔,然而我们真正需要的是将输入存储成数字(多少个空格):
8 years ago
```rust
let spaces = " ";
let spaces = spaces.len();
```
8 years ago
这里允许第一个 `spaces` 变量是字符串类型,而第二个 `spaces` 变量,它是一个恰巧与第一个变量同名的崭新变量,是数字类型。遮盖使我们不必使用不同的名字,如 `spaces_str``spaces_num`;相反,我们可以复用 `spaces` 这个更简单的名字。然而,如果尝试使用`mut`,如下所示:
8 years ago
```rust,ignore
let mut spaces = " ";
spaces = spaces.len();
```
8 years ago
会导致一个编译错误,因为改变一个变量的类型是不被允许的:
8 years ago
```
8 years ago
error[E0308]: mismatched types
--> src/main.rs:3:14
|
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected &str, found usize
|
= note: expected type `&str`
= note: found type `usize`
```
现在我们探索了变量如何工作,让我们看看更多的数据类型。