1、创建一个项目:cargo new variables
输入代码:
fn main() {
let x = 5;
println!("x的值是: {}", x);
x = 6;
println!("x的值是: {}", x);
}
然后运行:cargo build
然后你就会发现没法编译通过。
cannot assign twice to immutable variable
这就是上述报的错,意思是我们没法给一个不可变变量两次绑定值
这种规则让我们的代码变得非常清晰,只有你想让你的变量改变时,它才能改变,这样就不会造成心智上的负担,也给别人阅读代码带来便利。
在 Rust 中,可变性很简单,只要在变量名前加一个 mut
即可, 而且这种显式的声明方式还会给后来人传达这样的信息:嗯,这个变量在后面代码部分会发生改变。
fn main() {
let mut x = 5;
println!("x的值是: {}", x);
x = 6;
println!("x的值是: {}", x);
}
我们能轻易看出上面这行代码的结果:
x的值是: 5
x的值是: 6
使用下划线开头忽略未使用的变量
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 BUG。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时
你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头
fn main() {
let _x = 5;
let y = 10;
}
运行这行代码会有一个错误:help: if this is intentional, prefix it with an underscore: `_y` 意思是没有用到的变量,你可以在变量前加个下划线‘_y’
变量解构
let
表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容:
fn main() {
let (a, mut b): (bool,bool) = (true, false);
// a = true,不可变; b = false,可变
println!("a = {:?}, b = {:?}", a, b);
b = true;
assert_eq!(a, b);
}
解构式赋值
在 Rust 1.59 版本后,我们可以在赋值语句的左式中使用元组、切片和结构体模式了。
struct Struct {
e: i32
}
fn main() {
let (a, b, c, d, e);
(a, b) = (1, 2);
// _ 代表匹配一个值,但是我们不关心具体的值是什么,因此没有使用一个变量名而是使用了 _
[c, .., d, _] = [1, 2, 3, 4, 5];
Struct { e, .. } = Struct { e: 5 };
assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
}
变量和常量之间的差异
常量(constant)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异:
- 常量不允许使用
mut
。常量不仅仅默认不可变,而且自始至终不可变,因为常量在编译完成后,已经确定它的值。 - 常量使用
const
关键字而不是let
关键字来声明,并且值的类型必须标注。
下面是一个常量声明的例子,其常量名为 MAX_POINTS
,值设置为 100,000
。(Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性):
const MAX_POINTS: u32 = 100_000;
常量可以在任意作用域内声明,包括全局作用域,在声明的作用域内,常量在程序运行的整个过程中都有效。
变量遮蔽(shadowing)
Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的,如下所示:
fn main() {
let x = 5;
// 在main函数的作用域内对之前的x进行遮蔽
let x: i32 = x + 1;
{
// 在当前的花括号作用域内,对之前的x进行遮蔽
let x = x * 2;
println!("x在里面这个作用域的值为: {}", x);
}
println!("x的值为: {}", x);
}
x在里面这个作用域的值为: 12
x的值为: 6
这个程序首先将数值 5
绑定到 x
,然后通过重复使用 let x =
来遮蔽之前的 x
,并取原来的值加上 1
,所以 x
的值变成了 6
。第三个 let
语句同样遮蔽前面的 x
,取之前的值并乘上 2
,得到的 x
最终值为 12
。
这和 mut
变量的使用是不同的,第二个 let
生成了完全不同的新变量,两个变量只是恰好拥有同样的名称,涉及一次内存对象的再分配 ,而 mut
声明的变量,可以修改同一个内存地址上的值,并不会发生内存对象的再分配,性能要更好。
变量遮蔽的用处在于,如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量),就可以重复的使用变量名字,而不用绞尽脑汁去想更多的名字。
例如,假设有一个程序要统计一个空格字符串的空格数量:
// 字符串类型 let spaces = " ";
// usize数值类型 let spaces = spaces.len();
这种结构是允许的,因为第一个 spaces
变量是一个字符串类型,第二个 spaces
变量是一个全新的变量且和第一个具有相同的变量名,且是一个数值类型。所以变量遮蔽可以帮我们节省些脑细胞,不用去想如 spaces_str
和 spaces_num
此类的变量名;相反我们可以重复使用更简单的 spaces
变量名。如果你不用 let
:
let mut spaces = " ";
spaces = spaces.len();
运行一下,你就会发现编译器报错:
spaces = spaces.len();
^^^^^^^^^^^^ expected `&str`, found `usize`
显然,Rust 对类型的要求很严格,不允许将整数类型 usize
赋值给字符串类型。usize
是一种 CPU 相关的整数类型
我们可以这样改一下:
// 字符串类型
let spaces = " ";
// usize数值类型
let spaces = spaces.len();