不可变变量和可变变量及隐藏
不可变变量和可变变量
在Rust中,声明变量需要使用let
关键字,其使用格式大致如下:
1 | let var_name: type_name; |
变量声明后必须要赋值(初始化)后才可获取值,可以在声明的同时给变量赋值。
1 | let a: i32; |
类型名在大多数情况下可以不用写,编译器会自动根据使用的场景和所赋值推断出变量的类型:
1 | let a = 32; // 此时a是一个`i32`,默认设定整型字面值类型为`i32` |
注意,变量声后就必须赋值。
与许多语言不同,Rust需要在let
和变量名间加上mut
(mutable)来使其可变,不添加则不可变。
1 | let a; |
看完上面你可能会一脸懵:变量还分可变和不可变?
回答这个问题前,我先粗浅的解释mut
的作用:获取变量的修改权。这意味着:要想在Rust中修改变量,必须先获取修改权,修改权即修改变量的权限。因此在声明时加上mut
才能修改变量,此时就为可变变量,反之为不可变变量。这么做有几点好处:1. 防止变量被意外改变;2. 方便分析程序。
警告:上述解释非官方解释,修改权也不是官方术语,建议看看官方文挡(英|中)再下结论。
另外补充说明一下,修改权只能在以下四种情形获取或借用:
获取:声明变量时。
获取:变量以移动(
move
)的方式传参给函数时。借用:声明可变变量引用(
&mut T
)时。声明可变指针(
*mut T
)时。
修改权被借用后,直到引用被销毁前,都不能再次从原数据获取引用(因为已经借出去了),只能从借用了修改权的引用上借用修改权。
不可变变量的可变性
有人可能会疑惑,既然不可变,为何不将其当成常量?答:不可变变量在声明时所赋值可以是一个变量的值(例如ler r = 随机数();
(Rust没有随机数()
,我只是举个例子)),意味着每次程序运行时不可变变量的值都可以不同。因此不可变变量并不能当成常量。
如果你了解过C++的const
(readonly)、constexpr
,就可以这么认为:let a: i32
类似于const int a
、const a: i32
(const
在Rust中则用于声明常量)类似于constexpr int a
。
常量
<>声明一个常量需要使用const
关键字,常量名命名约定是全部单词大写,单词之间加下划线。
Rust中常量会在编译前计算出来(这也是let
和const
的差别所在),也就是说给常量赋值必须是常量值或常量表达式(能在编译时就能计算出结果),不能是变量,意味着其值在运行时都是确定的,而不像不可变变量一样运行时也可以不确定。
另外,声明常量必须写明常量的类型(既然是常量了,那肯定希望类型也是定下来的,不然运用时有可能会产生歧义或误用)。
1 | // ✔️,所赋值是字面值常量 |
隐藏(shadowing
)
注:在通过例子学Rust中,数据被隐藏称作数据被冻结,两者含义相同。
Rust允许声明变量名相同的变量,多个变量之间的类型可不同。使用变量名时,获取的变量为当前作用域上文及父作用域上文最新声明的变量。
上面第二句话可能难以听懂,不妨通过看代码来理解这一句话:
1 | fn main() { |
输出结果:
1 | x=2 |
原因不难解释:let
声明了一个新变量x,此时获取的变量就不再是原来的x而是新的x,直到离开新变量作用域范围后,才能获取原变量。这种特性就是隐藏(shadowing
),因为变量名相同,原变量就被新变量隐藏(shadowing
,其实我觉得应该译为遮蔽比较好理解)了,离开新变量的作用域后,原变量就不再被隐藏而可以被获取。
小课堂:有人希望修改一个不可变变量的值,他写了如下代码:
1 | let x = 1; //不可变变量,其所赋值可以是任意变量,这里假设为1 |
很明显上面的输出结果是1
而不是2
,很多人能指出错误的原因是新的x
隐藏了原有的x
,因此修改的是新的x
而不是旧的x
。但我更想指明一点:let mut
获取的修改权是新声明变量的修改权,不是所赋值的修改权。