不可变变量,可变变量,常量,默认
变量默认是不可改变的(immutable)
。这是 Rust
提供给你的众多优势之一,让你得以充分利用Rust
提供的安全性和简单并发性来编写代码。不过,你仍然可以使用可变变量。让我们探讨一下 Rust
为何及如何鼓励你利用不可变性,以及何时你会选择不使用不可变性。
当变量不可变时,一旦值被绑定一个名称上,你就不能改变这个值。为了对此进行说明,使用 cargo new variables
命令在 projects
目录生成一个叫做 variables
的新项目。
接着,在新建的 variables
目录,打开 src/main.rs
并将代码替换为如下代码,这些代码还不能编译,我们会首次检查到不可变错误(immutability error)
。
fn main() {
let a = 1;
println!("The value of a is: {a}");
a = 2;
println!("The value of a is: {a}");
}
保存并使用 cargo run
运行程序。应该会看到一条与不可变性有关的错误信息,如下输出所示:
$ cargo run
Compiling variables v0.1.0 (/projects/variables)
error[E0384]: cannot assign twice to immutable variable `a`
--> src/main.rs:4:5
|
2 | let a = 1;
| -
| |
| first assignment to `a`
| help: consider making this binding mutable: `mut a`
3 | println!("The value of a is: {a}");
4 | a = 2;
| ^^^^^ cannot assign twice to immutable variable
For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` due to previous error
这个例子展示了编译器如何帮助你找出程序中的错误。虽然编译错误令人沮丧,但那只是表示程序不能安全的完成你想让它完成的工作;
错误信息指出错误的原因是不能对不可变变量 a
二次赋值(cannot assign twice to immutable variable 'a' )
,因为你尝试对不可变变量 a
赋第二个值,编译器并且已给出解决方案(consider making this binding mutable: 'mut a')
Rust
在声明变量时,在变量前面加入 mut
关键字,变量就会成为可变绑定的变量。
让我们将 src/main.rs 修改为如下代码:
fn main() {
let mut a = 1;
println!("The value of a is: {a}");
a = 2;
println!("The value of a is: {a}");
}
现在运行这个程序,出现如下内容:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/variables`
The value of a is: 1
The value of a is: 2
通过mut
,允许把绑定到 a
的值从1
改成 2
。是否让变量可变的最终决定权仍然在你,取决于在某个特定情况下,你是否认为变量可变会让代码更加清晰明了。
类似于不可变变量,常量 (constants)
是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
首先,不允许对常量使用 mut
。常量不光默认不可变,它总是不可变。声明常量使用 const
关键字而不是 let
,并且 必须
注明值的类型。
常量可以在任何作用域中声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。
最后一个区别是,常量只能被设置为常量表达式,而不可以是其他任何只能在运行时计算出的值。
下面是一个声明常量的例子:
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
常量的名称是THREE_HOURS_IN_SECONDS
,它的值被设置为 60
(一分钟内的秒数)乘以 60
(一小时内的分钟数)再乘以 3
(我们在这个程序中要计算的小时数)的结果。Rust
对常量的命名约定是在单词之间使用全大写加下划线。编译器能够在编译时计算一组有限的操作,这使我们可以选择以更容易理解和验证的方式写出此值,而不是将此常量设置为值 10,800
。
我们可以定义一个与之前变量同名的新变量。Rustacean
们称之为第一个变量被第二个 隐藏(Shadowing)
了,这意味着当您使用变量的名称时,编译器将看到第二个变量。实际上,第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 let
关键字来多次隐藏,如下所示:
fn main() {
let a = 1;
let a = a + 2;
{
let a = a * 2;
println!("The value of a in the inner scope is: {a}");
}
println!("The value of a is: {a}");
}
这个程序首先将 a
绑定到值上。接着通过 let a = 创建了一个新变量 a
,获取初始值并加 1
,这样 a
的值就变成 3
了。然后,在使用花括号创建的内部作用域内,第三个 let 语句也隐藏了 x
并创建了一个新的变量,将之前的值乘以 2
,a
得到的值是 6
。当该作用域结束时,内部shadowing
的作用域也结束了,a
又返回到 3
。运行这个程序,它会有如下输出:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/variables`
The value of a in the inner scope is: 6
The value of a is: 3
隐藏与将变量标记为mut
是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 let
关键字,就会导致编译时错误。通过使用 let
,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。
mut
与隐藏的另一个区别是,当再次使用 let
时,实际上创建了一个新变量,我们可以改变值的类型,并且复用这个名字。例如,假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格,接下来我们想将输入存储成数字(多少个空格):
let spaces = " ";
let spaces = spaces.len();
第一个 spaces
变量是字符串类型,第二个 spaces
变量是数字类型。隐藏使我们不必使用不同的名字,如 spaces_str
和 spaces_num
;相反,我们可以复用 spaces
这个更简单的名字。然而,如果尝试使用 mut
,将会得到一个编译时错误,如下所示:
let mut spaces = " ";
spaces = spaces.len();
这个错误说明,我们不能改变变量的类型:
$ argo run
Compiling variables v0.1.0 (/projects/variables)
error[E0308]: mismatched types
--> src/main.rs:3:14
|
2 | let mut spaces = " ";
| ----- expected due to this value
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected `&str`, found `usize`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` due to previous error
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!