参考:https://kaisery.github.io/trpl-zh-cn/ch03-01-variables-and-mutability.html、https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html
不可变变量
含义
一旦值被绑定一个名称上,就不能改变这个值。Rust 中变量默认不可变。
例如: let x = 1;
优势
- 充分利用 Rust 提供的安全性和简单并发性来编写代码
- Rust 编译器不需要追踪一个值如何和在哪可能会被改变,从而使得代码易于推导
- 遮蔽机制 (shadowing):可以使用
let创建新的变量(或者称为重新定义变量、重新绑定变量)来改变值,甚至类型。如果新的值是变量改变前的类型,使用mut有相同的作用,然而前后类型不同时,使用mut是做不到的 ,例如
获取了字符串,然而关注的是其长度 (字符串可以扔掉),可以这样使用
let spaces = " ";let spaces = spaces.len(); // shadow 上一个 spaces ,并且类型从字符串变成了整型
而同样的场景使用 mut:
直接改变 spaces 的值不可行:
let mut spaces = " ";spaces = spaces.len(); // error: expected &str, found usize
重新绑定,虽然程序通过,但得到 not need to be mutable 警告:
let mut spaces = " "; // warning: variable does not need to be mutablelet mut spaces = spaces.len(); // warning: variable does not need to be mutable
let mut spaces = " ";let spaces = spaces.len(); // warning: variable does not need to be mutable
shadowing 和 scope 同时使用的例子:
fn main() {// 此绑定生存于 main 函数中let long_lived_binding = 1;// 这是一个代码块,比 main 函数拥有更小的作用域{// 此绑定只存在于本代码块let short_lived_binding = 2;println!("inner short: {}", short_lived_binding);println!("inner long: {}", long_lived_binding);// 此绑定*掩蔽*了外面的绑定let long_lived_binding = 5_f32;println!("inner long after shadowed: {}", long_lived_binding);}// 代码块结束// 报错!`short_lived_binding` 在此作用域上不存在// println!("outer short: {}", short_lived_binding);println!("outer long: {}", long_lived_binding);// 此绑定同样*掩蔽*了前面的绑定let long_lived_binding = 'a';println!("outer long after shadowed: {}", long_lived_binding);}
打印结果:
inner short: 2inner long: 1inner long after shadowed: 5outer long: 1outer long after shadowed: a
不可变与常量的区别
- 声明方式:不可变使用
let,通常编译器会提示使用 snakecase(使用 `连接单词,且每个单词小写);常量使用const且必须标注类型,通常编译器会提示使用 UPPER_SNAKE_CASE (使用_` 连接单词,且每个单词大写) - 表达式类型:不可变变量可接受多种表达式;常量只能被设置为常量表达式,而不能是函数调用的结果,或任何其他只能在运行时计算出的值
- 定义/绑定名称次数:不可变变量允许多次把名称绑定新的值,并且这种绑定可以是可变也可以是不可变(但会出现警告),但不能是常量绑定(即用 const 绑定);常量不允许把名称再次绑定值(无论是否为常量绑定),也就是只能定义一次名称。例如下面的例子:
不可变变量允许多次绑定:
fn main() {let x = 1;let x = 2; // x 可以再次进行绑定不可变值的操作,或者说此处的 x 隐藏了(shadow)上一行的 xlet mut x = 3; // x 可以再次进行绑定可变值的操作,但是出现 not need to be mutable 警告println!("{}", x);}

常量只允许一次绑定:
fn main() {const Y: u32 = 2;let Y: u32 = 1; // Y 不可以再次进行 let 绑定值的操作,即便标注相同的类型。常量不能当做变量使用。println!("{}", Y);}

fn main() {const Y: u32 = 2;const Y: u32 = 1; // Y 不可以再次进行 const 绑定值的操作,即便标注相同的类型。常量只能定义一次(must be defined only once)。println!("{}", Y);}

不可变量、常量、静态常/变量的区别:见 “unsafe Rust”
20.1 unsafe Rust
可变变量
含义
允许改变值、表明其他代码将会改变这个变量值的意图。在变量名之前加 mut 来使其可变。
例如 let mut x = 1;
优势
- 与只用不可变变量相比,可变性会让代码更容易编写
- 使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快
(可读性)对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解
劣势
牺牲性能
- 更容易出现 bug (与编译器作斗争)
变量先声明
可以先声明(declare)变量绑定,后面才将它们初始化(initialize)。但是这种做法很 少用,因为这样可能导致使用未初始化的变量。
编译器禁止使用未经初始化的变量,因为这会产生未定义行为(undefined behavior)。fn main() {// 声明一个变量绑定let a_binding;{let x = 2;// 初始化一个绑定a_binding = x * x;}println!("a binding: {}", a_binding);let another_binding;// 报错!使用了未初始化的绑定println!("another binding: {}", another_binding);// 改正 ^ 注释掉此行another_binding = 1;println!("another binding: {}", another_binding);}
