2.1 变量和可变性
Rust的变量不同于其他编程语言的变量,其本质上是一种绑定语义,即将一个变量名与一个值绑定在一起。变量名和值建立关联关系。另一个不同点是变量默认是不可改变的,这是为了让开发者能充分利用Rust提供的安全性来编写代码。当然,Rust也提供了使用可变变量的方式。下面具体介绍变量声明和可变变量以及常量的使用。
2.1.1 变量声明
Rust通过let关键字声明变量,变量遵循先声明后使用的原则。变量声明的语法如下:
1 let x: i8 = 1; 2 let x = 1; // 等价于:let x: i32 = 1;
变量声明以let关键字开头,x为变量名,变量名后紧跟冒号和数据类型。Rust编译器具有变量类型的自动推导功能。在可以根据赋值类型或上下文信息推导出变量类型的情况下,冒号和数据类型可以省略。
变量名必须由字母、数字、下划线组成,字母区分大小写且不能以数字开头,也不能只有下划线。Rust中下划线是一种特殊的标识符,其含义是“忽略这个变量”,后续章节中会用到这个标识符。
2.1.2 变量的可变性
let声明的变量默认是不可变的,在第一次赋值后不能通过再次赋值来改变它的值,即声明的变量是只读状态。代码清单2-1中,第3行代码尝试对不可变变量x进行二次赋值,即进行写操作,这是不被允许的。编译代码,将会抛出cannot assign twice to immutable variable `x`的错误提示。根据错误提示信息可知,不能对不可变变量进行二次赋值。
代码清单2-1 对不可变变量二次赋值
1 fn main() { 2 let x = 3; 3 x = 5; 4 println!("x: {}", x); 5 }
在变量名的前面加上mut关键字就是告诉编译器这个变量是可以重新赋值的。代码清单2-2中,第2行代码中的mut关键字将变量x声明为可变变量,即x为可写状态。编译代码,不再出现上述错误。
代码清单2-2 对可变变量二次赋值
1 fn main() { 2 let mut x = 3; 3 x = 5; 4 println!("x: {}", x); 5 } 6 7 // x: 5
将变量区分为可变与不可变两种状态,是Rust语言安全性的一大体现。因为在大型程序中,如果有一部分代码假设一个变量为不可变的只读状态,而另一部分代码却尝试改变这一预设为可变变量,那么第一部分代码就有可能以不可预料的方式运行,这种bug将很难被跟踪调试。而Rust编译器保证了如果一个变量声明为不可变变量,那它就真的不会变。当然,变量的可变性也是非常有用的,只需在变量名之前加mut关键字即可。这种声明方式除了允许改变该变量值之外,还明确传达了允许其他代码改变这个变量值的意图。
变量的可变性声明是初学者进行Rust编程时最容易出错的地方,在实战章节还会对此做重点讲解。
2.1.3 变量遮蔽
Rust允许在同一个代码块中声明一个与之前已声明变量同名的新变量,新变量会遮蔽之前的变量,即无法再去访问前一个同名的变量,这样就实现了变量遮蔽。
代码清单2-3中,第2行代码声明变量x并与值3绑定,第3行代码获取x的值加2得到5,并通过let关键字声明新的变量x与值5绑定。此时,第一个变量x已被遮蔽,当前x的值变成了5。第4行代码获取x的值乘以2得到10,并通过let关键字再次声明新的变量x与值10绑定,当前x的值变成了10。第7行代码声明了新的变量x与字符串“Hello, Rust!”绑定,此时变量x已由i32类型变为&str类型。
代码清单2-3 变量遮蔽
1 fn main() { 2 let x = 3; 3 let x = x + 2; 4 let x = x * 2; 5 println!("x: {}", x); 6 7 let x = "Hello, Rust!"; 8 println!("x: {}", x); 9 } 10 11 // x: 10 12 // x: Hello, Rust!
变量遮蔽的实质是通过let关键字声明了一个新的变量,只是名称恰巧与前一个变量名相同而已,但它们是两个完全不同的变量,处于不同的内存空间,值可以不同,值的类型也可以不同。变量遮蔽常应用于一些特殊场景,在后续实战章节中会用到。
2.1.4 常量
常量是指绑定到一个标识符且不允许改变的值,一旦定义后就没有任何方法能改变其值了。Rust声明常量的语法如下:
const MAX_NUM: u32 = 1024;
Rust使用const关键字来声明常量。常量名通常是大写字母,且必须指定常量的数据类型。常量与不可变变量的区别主要在于:
1)常量声明使用const关键字,且必须注明值的类型。
2)通过变量遮蔽的方式可以让不可变变量的值改变(本质上是新的变量,只是同名而已)。但是,常量不能遮蔽,不能重复定义。也就是说,不存在内层或后面作用域定义的常量去遮蔽外层或前面定义的同名常量的情况。常量一旦定义后就永远不可变更和重新赋值。
3)常量可以在任何作用域中声明,包括全局作用域。在声明它的作用域中,常量在整个程序生命周期内都有效,这使得常量可以作为多处代码共同使用的全局范围的值。
4)常量只能被赋值为常量表达式或数学表达式,不能是函数返回值,或是其他在运行时才能确定的值。