理解Rust中的变量赋值
Rust中使用let声明变量:
fn main(){
// 声明变量name并初始化赋值
let name = "junmajinlong.com";
println!("{}", name); // println!()格式化输出数据
}
Rust会对未使用的变量发出警告信息。如果确实想保留从未被使用过的变量,可在变量名前加上_前缀。
fn main(){
let name = "junmajinlong.com";
println!("{}", name);
let gender = "male"; // 警告,gender未使用
let _age = 18; // 加_前缀的变量不被警告
}
Rust允许声明未被初始化(即未被赋值)的变量,但不允许使用未被赋值的变量。多数情况下,都是声明的时候直接初始化的。
fn main() {
let name; // 只声明,未初始化
// println!("{}", name); // 取消该行注释,将编译错误
name = "junmajinlong.com";
println!("{}", name);
}
Rust允许重复声明同名变量,后声明的变量将遮盖(shadow)前面已声明的变量。需注意的是,遮盖不是覆盖,被遮盖的变量仍然存在,而如果是被覆盖则不再存在(也即,覆盖时,原数据会被销毁)。
fn main() {
let name = "junmajinlong.com";
// 注释下行,将警告:name变量未被使用
// 因为name仍然存在,只是被遮盖了
println!("{}", name);
let name = "gaoxiaofang.com"; // 遮盖已声明的name变量
println!("{}", name);
}
变量遮盖示意图:
注:下图内存布局并不完全正确,此图仅为说明变量遮盖
+---------+ +--------------------+
| Stack | | Heap |
+---------+ +--------------------+
name --> | 0x56789 | ---> | "gaoxiaofang.com" |
| | +--------------------+
name --> | 0x01234 | ---> | "junmajinlong.com" |
+---------+ +--------------------+
变量初始化后,默认不允许再修改该变量。注意,修改变量是直接给变量赋值,而不是再次let声明该变量,再次声明变量是允许的,它会遮盖原变量。
fn main() {
let name = "junmajinlong.com";
// 取消下行注释将编译错误,默认不允许修改变量
// name = "gaoxiaofang.com";
let name = "gaoxiaofang.com"; // 再次声明变量,遮盖变量
println!("{}", name);
}
如果想要修改变量的值,需要在声明变量时加上mut标记(mutable)表示该变量是可修改的。
fn main() {
let mut name = "junmajinlong.com";
println!("{}", name);
name = "gaoxiaofang.com"; // 修改变量
println!("{}", name);
}
Rust不仅对未被使用过的变量发出警告,还对赋值过但未被使用过的值发出警告。比如变量赋值后,尚未读取该变量,就重新赋值了。
fn main() {
let mut name = "junmajinlong.com"; // 警告值未被使用过
name = "gaoxiaofang.com";
println!("{}", name);
}
Rust是静态语言,声明变量时需指定该变量将要保存的值的数据类型,这样编译器编译时才知道为该变量将要保存的数据分配多少内存、允许存放什么类型的数据以及如何存放数据。但Rust编译器会根据所保存的值来推导变量的数据类型,推导得到确定的数据类型之后(比如第一次为该变量赋值之后),就不再允许存放其他类型的数据。
fn main() {
// 根据保存的值推导数据类型
// 推导结果:变量name为 &str 数据类型
let mut name = "junmajinlong.com";
//name = 32; // 再让name保存i32类型的数据,报错
}
当Rust无法推导类型时,或者声明变量时就明确知道该变量要保存声明类型的数据时,可明确指定该变量的数据类型。
fn main() {
// 指定变量数据类型的语法:在变量名后加": TYPE"
let age: i32 = 32; // 明确指定age为i32类型
println!("{}", name);
// i32类型的变量想存储u8类型数据,不允许
// age = 23_u8;
}
虽然Rust是基于表达式的语言,但变量声明的let代码是语句而非表达式。这意味着let操作没有返回值,因此无法使用let来连续赋值。
fn main(){
let a = (let b = 1); // 错误
}
可以使用tuple的方式同时为多个变量赋值,并且可以使用下划线_占位表示忽略某个变量的赋值过程。
#![allow(unused)]
fn main() {
// x = 11, y = 22, 忽略33
let (x, y, _) = (11, 22, 33);
}
事实上,_占位符比想象中还更会【偷懒】,其他语言中_表达的含义可能是丢弃其赋值结果(甚至不丢弃),但Rust中的_会直接忽略变量赋值的过程。这导致了这样一种看似奇怪的现象:使用普通变量名会导致报错的变量赋值行为,使用_却不会报错。
例如,下面(1)不会报错,而(2)会报错。这里涉及到了后面所有权转移的内容,如果看不懂请先跳过,只需记住结论:_会直接忽略赋值的过程。
#![allow(unused)]
fn main() {
// (1)
let s1 = "junmajinlong.com".to_string();
let _ = s1;
println!("{}", s1); // 不会报错
// (2)
let s2 = "junmajinlong.com".to_string();
let ss = s2;
println!("{}", s2); // 报错
}
最后要说明的是,Rust中变量赋值操作实际上是Rust中的一种模式匹配,在后面的章节中将更系统、更详细地介绍Rust模式匹配功能。