变量与可变性
Rust中的变量特性
Rust中变量的定义格式:
let 变量名: 数据类型 = 对应的值; //不可变的变量
let mut 变量名: 数据类型 = 对应的值; //可变的变量
在Rust中,变量的声明默认是不可变的。这是 Rust 提供给你的众多优势之一,让你得以充分利用 Rust 提供的安全性和简单并发性来编写代码。
不过也可以使用一些方法把变量的不变性打破,让变量是可变的。要结合实际场景使用变量的不变与可变。
当变量不可变时,一旦值被绑定一个名称上,你就不能改变这个值。
例如:
fn main() {
let x = 3; // 当前x是一个不可变的变量,已经绑定它的值为3
x = 5; //试图改变x的值,则在编译阶段就会报错。
}
在命令行使用cargo check时就会报以下的错:
error[E0384]: cannot assign twice to immutable variable `x`
即不可以再次赋值给不可变的变量x.
因为使用let声明的变量默认是不可变的,所以x一开始赋值是3,所以直到x的生命周期结束了都不可以修改x的值。
如果想修改变量的值,那就需要声明变量是可变的,对应的操作也很简单,只需要在let和变量名之间插入mut关键字即可。
mut是mutable的缩写
例如:
fn main() {
let mut x = 3; // 当前x是一个可变的变量,已经绑定它的值为3
x = 5; //试图改变x的值,没有任何问题,因为已经声明x是可变的变量。
}
Rust中对变量的定义可以采用以下方式:
//let 变量名 = 值+类型;
let i = 3i32; //等效let i: i32 = 3;
Rust中的常量
在Rust中,很奇怪的现象就是,变量默认是不可变的,而常量也是不可以改变的,但是这两个是不一样的东西。
在Rust中变量默认不可变,但是可以通过加一个mut关键字就可以修改变量的值。在Rust中,对变量还有“隐藏”的操作,后面会讲到。
但是常量一旦定义了就不可以改变。
简单理解:(在Rust中)
常量:不可变的量
变量:默认是不可变的变量
常量一些规范:
- 常量一般使用全大写加下划线组合命名
- 不可以使用变量来赋值给常量
- 一旦定义就不可改变
例子:
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
常量的名称是 THREE_HOURS_IN_SECONDS
,它被赋值为60 * 60 * 3
的结果 。编译器能够在编译时计算一组有限的操作,这使我们可以选择以更容易理解和验证的方式写出此值,而不是将此常量设置为值10,800。有关声明常量时可以使用哪些操作的详细信息,请参阅 Rust Reference 的常量求值部分。
在声明它的作用域之中,常量在整个程序生命周期中都有效,此属性使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
将遍布于应用程序中的硬编码值声明为常量,能帮助后来的代码维护人员了解值的意图。如果将来需要修改硬编码值,也只需修改汇聚于一处的硬编码值。
Rust中的隐藏
我们可以定义一个与之前变量同名的新变量,Rustacean 们称之为第一个
变量被第二个
隐藏(Shadowing) 了,这意味着当您使用变量的名称时,编译器将看到第二个变量。实际上,第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 let
关键字来多次隐藏,如下所示
fn main() {
let x = 5; // x1
let x = x + 1; // x2 = x1 + 1;
// x1已经被遮蔽,往后看到的x默认是x2
{ // 引入一个新的域
let x = x * 2; // x3 = x2 * 2;
// x2被遮蔽,往后看到的x默认是x3
println!("The value of x in the inner scope is: {x}"); //打印x3的值
} // 出域,x3的生命周期结束,x2被回显,往后看到的x默认是x2
println!("The value of x is: {x}"); // 打印x2的值
} // 出域,x2的生命周期结束,x1被回显,但x1的生命周期也结束,所以x1和x2都死亡,被栈回收内存
程序一开始,使用一个x变量绑定到5身上,即使用5赋值给x变量(不可变),将x+1的值赋值给名字和x相同的变量(不可变)中,于是原来的x就被遮蔽,在后续的代码中如果使用到了x则是使用第二个x变量,直到该x被遮蔽或者生命周期结束。
隐藏和mut的区别
隐藏与将变量标记为 mut
是有区别的。
-
当不小心尝试对变量重新赋值时,如果没有使用
let
关键字,就会导致编译时错误。 -
通过使用
let
,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。 -
当再次使用
let
时,实际上创建了一个新变量,我们可以改变值的类型,并且复用这个名字。
例如:
fn main() {
let a = 5;
// a = 9; 编译错误,a是不可变的变量,不可重新赋值
test(a);
println!("main: a = {}", a);
}
fn test(a: i32) {
{
let a = a + 2; // 可以再次let隐藏变量,即第二个a遮蔽了第一个a
println!("test 1: a = {}", a); //7
} //第二个a死亡
{
let mut a = a + 2; //因为第二个a死亡了,所以第一个a回显,回显后再次被第三个a遮蔽,并且第三个a是可变的
let b = a + 10;
a = 8;
println!("test 2: a = {}\tb = {}", a, b);
} //第三个a死亡
{
let a: String = String::from("Hello"); //第四个a遮蔽第一个a并改变了类型
println!("test 3: a = {}", a);
} //第四个a死亡
println!("test: a = {}", a); //第一个a回显
}
结果:
test 1: a = 7
test 2: a = 8 b = 17
test 3: a = Hello
test: a = 5
main: a = 5
因为mut是声明变量是可变的,但是它的可变只是值变了,但类型并不能改变,而let隐藏这是使用一个新的变量来遮蔽原来的变量,新的变量当然可以是新的值也可以是新的类型。
let mut spaces = " ";
spaces = spaces.len();
编译器报错:
error[E0308]: mismatched types
|
2 | let mut spaces = " ";
| ----- expected due to this value
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected `&str`, found `usize`
现在我们已经了解了变量如何工作,让我们看看变量可以拥有的更多数据类型。