Updated src/programming_concepts/variables_and_mutability.md'.

This commit is contained in:
Hector PENG
2026-03-06 14:25:36 +08:00
parent eac84d25b4
commit 5edb481b02
4 changed files with 39 additions and 45 deletions

View File

@@ -0,0 +1,6 @@
[package]
name = "shadowing"
version = "0.1.0"
edition = "2024"
[dependencies]

View File

@@ -0,0 +1,4 @@
fn main() {
let mut spaces = " ";
spaces = spaces.len();
}

View File

@@ -146,15 +146,15 @@ fn main() {
```rust
let apples = 5;
let apples = 5;
```
这一行创建了个名为 `apples` 的新变量,并将其与值 `5` 绑定。在 Rust 中变量默认是不可变的immutable这意味着一旦我们赋予变量某个值那么该值就不会改变。我们将在第 3 章 [“变量和可变性”](programming_concepts/variables_and_mutability.md) 小节中,详细讨论这一概念。要使某个变量可变,我们就要在该变量的名字前添加 `mut` 关键字:
```rust
let apples = 5; // 不可变immutable
let mut bananas = 5; // 可变mutable
let apples = 5; // 不可变immutable
let mut bananas = 5; // 可变mutable
```
> **注意**:其中的 `//` 语法会开始一条注释一直持续到行尾。Rust 会忽略注释中的所有内容。我们将在 [第 3 章](programming_concepts/comments.md) 详细讨论注释。
@@ -199,7 +199,7 @@ let mut bananas = 5; // 可变mutable
```rust
io::stdin().read_line(&mut guess).expect("读取输入失败");
io::stdin().read_line(&mut guess).expect("读取输入失败");
```
不过,一个长行难于阅读,所以最好将其分开。在咱们使用 `.method_name()` 语法,调用某个方法时,引入一个换行符,以及另外的空白,来帮助拆分长行,通常是明智之举。现在我们来讨论一下这行的作用。
@@ -254,10 +254,10 @@ Rust 警告咱们未曾使用自 `read_line` 返回的 `Result` 值,表明程
```rust
let x = 5;
let y = 10;
let x = 5;
let y = 10;
println! ("x = {x} 而 y + 2 = {}", y + 2);
println! ("x = {x} 而 y + 2 = {}", y + 2);
```
此代码将打印出 `x = 5 而 y + 2 = 12`

View File

@@ -81,7 +81,7 @@ x 的值为5
x 6
```
在使用 `mut` 后,我们就可以将绑定到 `x` 的值从 `5` 修改为 `6` 了。最终,决定是否要使用可变取决于咱们自己,取决于在特定情形下咱们认为哪种最清楚明了。
在使用 `mut` 后,我们就可以将绑定到 `x` 的值从 `5` 修改为 `6` 了。最终,决定是否要使用可变取决于咱们自己,取决于咱们认为在特定情形下哪种最清楚明了。
## 声明常量
@@ -141,22 +141,19 @@ x 的值为6
下面是个常量声明的示例:
```rust
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
```
这个常量的名字是 `THREE_HOURS_IN_SECONDS`,其值被设为 60一分钟的秒数乘以 60一小时的分钟数再乘以 3程序中要计算的小时数的结果。Rust 的常量命名约定,是使用全大写字母,单词之间使用下划线。编译器可以在编译时计算一个有限的运算集,这让我们可以选择以一种更容易理解和验证的方式写出这个值,而不是将这个常量设为值 10,800。有关在声明常量时,可以使用哪些运算的更多信息,请参阅 [Rust 参考手册中,关常量求值的小节](https://doc.rust-lang.org/reference/const_eval.html)。
这个常量的名字是 `THREE_HOURS_IN_SECONDS`,其值被设为 60一分钟的秒数乘以 60一小时的分钟数再乘以 3这个程序中要计算的小时数的结果。Rust 的常量命名约定,是使用全大写字母,单词之间带有下划线。编译器能够在编译时计算一个有限的运算集,这让我们可以选择以一种更容易理解和验证的方式写出这个值,而不是将这个常量设为值 10,800。有关在声明常量时使用哪些运算的更多信息,请参阅 Rust 参考手册中,[关常量求值的小节](https://doc.rust-lang.org/reference/const_eval.html)。
常量在程序运行的整个过程中,在其声明的范围内都有效。这一属性使得常量对于程序域中,多个部分可能需要了解的值,例如游戏中允许任何玩家获得的最大点数或光速,会非常有用
常量在程序运行的整个时间内,在他们被声明的范围内都有效。这一属性使得常量对于咱们程序域中,程序的多个部分可能需要了解的值非常有用,例如游戏中允许任何玩家获得的最大点数或光速
将整个程序中要用到的一些值,命名为常量,有助于向未来的代码维护者传达该值的含义。此外,如果将来需要更新硬编码值,只需更改咱们代码中的一处即可
给咱们程序整个范围中都要用到的一些硬编码值作为常量命名,有助于向代码的未来维护者传达该值的含义。此外,当硬编码值今后需要更新时,咱们需更修改的只有一处,这也很有帮助
## 遮蔽
**Shadowing**
如同咱们曾在 [第 2 章](../Ch02_Programming_a_Guessing_Game.md#将猜数与秘数相比较) 的猜数游戏教程中所看到的咱们可以声明出一个与先前的某个变量同名的新变量。Rustaceans 会说,第一个变量是被第二个 *遮蔽了shadowd*,这意味着当你使用这个变量的名字时,编译器将看到的,是第二个变量。实际上,第二个变量覆盖了第一个,将变量名的任何使用,都带到自己身上,直到他自己被遮蔽,或作用域结束。像下面这样,通过使用相同的变量名,和重复使用 `let` 关键字,咱们便可以遮蔽某个变量:
正如咱们曾在 [第 2 章](../Ch02_Programming_a_Guessing_Game.md#比较猜数与秘密数) 的猜数游戏教程中所看到的咱们可以声明一个与某个先前变量同名的新变量。Rustaceans 会说,第一个变量是被第二个 *遮蔽了shadowed*,这意味着当咱们使用这个变量的名字时,编译器将看到的是第二个变量。实际上,第二个变量掩盖了第一个,将这一变量名的任何用途都归为自身,直到他自己被遮蔽或作用域结束。通过使用相同变量的名字,并重复使用 `let` 关键字,咱们便可遮蔽某个变量,如下所示:
文件名:`src/main.rs`
@@ -176,71 +173,58 @@ fn main() {
}
```
```console
内部作用域中 x 的值为12
x 的值为6
```
> 注意:遮蔽特性的使用,不需要 `mut` 关键字。
这个程序首先 `x`,绑定到 5 的值。然后通过重复 `let x =`创建出一个新变量 `x`将原来的值加上 `1`这样 `x` 的值就是 `6`。然后,在大括号创建的一个内层作用域中,第三个 `let` 语句也对 `x` 进行了遮蔽处理,并创建一个新变量,将先前值乘以 `2`使 `x` 的值为 `12`。当这个作用域结束时,内部的遮蔽结束,`x` 回到 `6`。当我们运行这个程序时,他将输出下内容:
这个程序首先会绑定 `x` 到一个 5 的值。然后,他通过重复 `let x =` 创建出一个新变量 `x`取原先值并加 `1`因此 `x` 的值 `6`。然后,在一个以大括号创建的内层作用域中,第三个 `let` 语句同样遮蔽了 `x` 创建一个新变量,将先前值乘以 `2` 使 `x` 的值为 `12`。当作用域结束时,内部的遮蔽结束`x` 回到 `6`。当我们运行这个程序时,他将输出下内容:
```console
$ cargo run
Compiling variables v0.1.0 (C:\tools\msys64\home\Lenny.Peng\rust-lang-zh_CN\projects\variables)
Finished dev [unoptimized + debuginfo] target(s) in 1.03s
Running `target\debug\variables.exe`
Compiling shadowing v0.1.0 (/home/hector/rust-lang-zh_CN/projects/shadowing)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
Running `target/debug/shadowing`
内部作用域中 x 的值为12
x 的值为6
```
遮蔽操作不同于将变量构造为 `mut`,因为如果我们不小心在没有使用 `let` 关键字的情况下,将变量重新赋值,咱们就会得到一个编译时报错。而通过使用 `let` 关键字,我们可对某个值执行一些变换,在这些变换完成后,该变量不可变。
遮蔽不同于将变量构造为 `mut`,因为我们不小心在没有使用 `let` 关键字下,尝试重新赋值到这个变量时,咱们得到一个编译时报错a compile-time error。而通过使用 `let` 关键字,我们可对某个值执行一些变换,在这些变换完成后,该变量不可变。
`mut` 与遮蔽的另一区别是,当我们再次使用 `let` 关键字时,我们实际上创建了个新变量,因此我们可以改变值的类型,而可以重复使用这个同样的名字。例如,假设咱们的程序要求用户通过输入一些空格字符,给出他们想要的某些文本间多少个空格,然后我们打算将这个输入存储为一个数字:
`mut` 与遮蔽的另一区别是,由于当我们再次使用 `let` 关键字时,我们实际上创建了个新变量,因此我们可以改变这个值的类型,却重用了同一名字。例如,假设咱们的程序要求用户通过输入一些空格字符,给出他们想要些文本间多少个空格,然后我们打算将输入存储为数字:
```rust
let spaces = " ";
let spaces = spaces.len();
let spaces = " ";
let spaces = spaces.len();
```
第一个 `spaces` 变量,属于字符串类型,而第二个 `spaces` 变量,则是数字类型。遮蔽就这样,让我们不必使用不同的名称,如 `spaces_str``spaces_num`;相反,我们可以重复使用这个更简单的 `spaces` 名称。然而,如果我们尝试使用 `mut` 来实现这一点,如下所示,咱们就会收到一个编译时报错:
第一个 `spaces` 变量属于字符串类型,而第二个 `spaces` 变量则是数字类型。遮蔽因此而让我们不必想出不同的名称,比如 `spaces_str``spaces_num`;相反,我们可以重用这个更简单的 `spaces` 名字。然而,当我们尝试对此使用 `mut`,如下所示,咱们将得到一个编译时报错:
```rust
let mut spaces = " ";
spaces = spaces.len();
let mut spaces = " ";
spaces = spaces.len();
```
该报错表明,我们允许改变变量的类型:
该报错表明,我们未被允许改变变量的类型:
```console
$ cargo run
Compiling variables v0.1.0 (C:\tools\msys64\home\Lenny.Peng\rust-lang-zh_CN\projects\variables)
Compiling shadowing v0.1.0 (/home/hector/rust-lang-zh_CN/projects/shadowing)
error[E0308]: mismatched types
--> src\main.rs:3:14
--> src/main.rs:3:14
|
2 | let mut spaces = " ";
| ------ expected due to this value
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected `&str`, found `usize`
|
help: try removing the method call
|
3 - spaces = spaces.len();
3 + spaces = spaces;
|
For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` (bin "variables") due to previous error
error: could not compile `shadowing` (bin "shadowing") due to 1 previous error
```
现在我们已经探讨了变量的工作原理,我们来看看他们可以有的更多数据类型。
现在我们已经探讨了变量的工作原理,我们来看看他们可以有的更多数据类型。
End