2. 变量与可变性 创建变量并演示let与mut的区别,理解不可变变量和可变变量的使用
任务说明
本案例通过创建变量并演示 let 与 mut 的区别,理解 Rust 中不可变变量与可变变量的使用场景。你将编写一个包含不可变变量、可变变量、常量以及变量遮蔽(shadowing)的完整程序,并观察编译器的行为。
核心概念与代码实现
Rust 中变量默认不可变,这是保证内存安全与并发安全的重要设计。使用 let 声明的变量不可重新赋值;添加 mut 关键字后变量变为可变。常量使用 const 声明,必须在编译期确定值,且类型必须显式标注。变量遮蔽允许使用同名 let 重新声明变量,新变量会遮蔽旧变量。
创建 src/main.rs 文件,输入以下代码:
// src/main.rs
// 演示 let 与 mut 的区别、常量以及变量遮蔽
// 常量:编译期常量,必须显式标注类型
const MAX_POINTS: u32 = 100_000;
fn main() {
// 1. 不可变变量:默认绑定,不能重新赋值
let x = 5;
println!("不可变变量 x 的值: {}", x);
// 下面这行如果取消注释,编译会报错:cannot assign twice to immutable variable
// x = 6;
// 2. 可变变量:使用 mut 关键字声明
let mut y = 10;
println!("可变变量 y 的初始值: {}", y);
y = 20; // 允许重新赋值
println!("可变变量 y 修改后的值: {}", y);
// 3. 常量使用
println!("常量 MAX_POINTS 的值: {}", MAX_POINTS);
// 4. 变量遮蔽:允许同名重新声明,新变量遮蔽旧变量
let z = 1;
println!("第一次声明 z: {}", z);
let z = z + 1; // 遮蔽:基于旧值创建新变量
println!("遮蔽后 z: {}", z);
// 遮蔽后的变量默认仍不可变
// z = 10; // 取消注释会报错
// 5. 遮蔽与 mut 结合:遮蔽可以改变可变性
let mut spaces = " "; // 字符串类型
println!("spaces 初始值: '{}'", spaces);
spaces = spaces.len(); // 错误!类型不匹配,无法赋值
// 正确做法:使用遮蔽改变类型
let spaces = spaces.len(); // 遮蔽:从字符串变为数字
println!("遮蔽后 spaces 的长度: {}", spaces);
}
编译与运行
在项目目录下执行以下命令:
cargo run
如果使用 rustc 直接编译:
rustc src/main.rs && ./main
预期输出
程序正常编译并运行后,控制台输出如下(与代码逻辑严格对应):
不可变变量 x 的值: 5
可变变量 y 的初始值: 10
可变变量 y 修改后的值: 20
常量 MAX_POINTS 的值: 100000
第一次声明 z: 1
遮蔽后 z: 2
spaces 初始值: ' '
遮蔽后 spaces 的长度: 3
验证方法与判定标准
1. 编译通过:执行 cargo run 无错误,输出与上述预期一致,说明 let、mut、const 和遮蔽的基本用法正确。
2. 不可变验证:将代码中 // x = 6; 的注释取消,重新编译,观察编译器报错 cannot assign twice to immutable variable,证明不可变变量不可重新赋值。
3. 类型不匹配验证:将 spaces = spaces.len(); 的注释取消(保留 let spaces = spaces.len(); 注释),编译报错 mismatched types,证明不可变变量不能改变类型,而遮蔽可以。
4. 可变性切换验证:在遮蔽后的 let spaces = spaces.len(); 之后添加 spaces = 5;,编译报错,证明遮蔽产生的新变量默认不可变。
使用场景说明
不可变变量适用于值在整个生命周期内不会改变的场景,如配置值、计算结果缓存,编译器可据此进行优化并防止误修改。可变变量适用于需要迭代更新状态的场景,如计数器、累加器、循环变量。常量适用于全局配置、数学常数等编译期确定的值。变量遮蔽常用于值类型转换或阶段性计算,避免为中间结果创建大量不同名称的变量。