第4章:变量与可变性——let、mut、shadowing 与 const
变量,是编程语言里最基础的概念之一。但 Rust 里的变量,跟 JavaScript 或 Python 不太一样。它有一套自己的规矩,说白了就是:默认不可变,想变你得主动说一声。
我第一次接触 Rust 时,也被这个设计惊到了。心想:变量不就是用来变的吗?后来写多了才明白,这个设计其实是在帮你减少 bug。嗯,咱们慢慢聊。
4.1 默认不可变:let 的“静”与“动”
在 Rust 里,用 let 声明一个变量,默认是不可变的。什么意思?就是一旦赋值,就不能再改了。
fn main() {
let x = 5;
x = 6; // 编译错误:不能对不可变变量二次赋值
}
你可能会问:为什么要有这种限制?
我个人习惯把这种设计理解为“安全锁”。想象一下,你写了一个大型项目,变量被到处引用。如果某个变量突然被改了,你很难追踪是谁改的、什么时候改的。Rust 的默认不可变,就是帮你锁住这个变量,除非你明确说“我要改它”。
4.2 可变性:mut 的“开锁”操作
那如果我真的需要改变一个变量的值呢?很简单,加个 mut 关键字就行。
fn main() {
let mut y = 10;
y = 20; // 没问题,y 是可变的
println!("y = {}", y);
}
mut 是 mutable 的缩写,意思是“可变的”。它就像一把钥匙,你主动告诉 Rust:“这个变量我后面要改,你别拦我。”
我在项目中遇到过这样一个场景:写一个计数器,需要不断累加。如果不用 mut,每次都得重新声明一个新变量,代码会变得很啰嗦。这时候 mut 就派上用场了。
mut。只在确实需要改变变量值的地方使用。过度使用 mut 会降低代码的可读性和安全性。
4.3 变量隐藏(Shadowing)——同名变量的“覆盖”艺术
Rust 里有一个很有意思的特性:变量隐藏。你可以用同一个变量名,声明一个全新的变量,新变量会“覆盖”掉旧的。
fn main() {
let x = 5;
let x = x + 1; // 隐藏了之前的 x
let x = x * 2; // 再次隐藏
println!("x = {}", x); // 输出 12
}
注意,这里不是修改了原来的 x,而是创建了一个新的 x。旧的 x 已经失效了。
我曾经在解析用户输入时用过这个技巧。比如先读一个字符串,然后把它转换成数字,再用同一个变量名保存数字。这样代码更简洁,也更容易理解。
fn main() {
let input = "42";
let input: i32 = input.parse().expect("解析失败");
println!("数字是: {}", input);
}
这里 input 从字符串变成了整数,类型都变了。这就是 shadowing 的威力——可以改变变量的类型。而 mut 做不到这一点,mut 只能改变值,不能改变类型。
mut:修改同一个变量的值,类型不能变。- shadowing:创建新变量,可以改变类型,旧变量被隐藏。
4.4 常量(const)——真正的“不变”
常量跟不可变变量有点像,但更严格。用 const 声明,值必须在编译时就确定,而且永远不能变。
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159;
常量有几个特点:
- 必须显式标注类型(比如
u32、f64)。 - 命名惯例是全大写加下划线。
- 可以在任何作用域声明,包括全局作用域。
- 值必须是编译期常量表达式,不能是运行时计算的结果。
我在项目中常用常量来定义配置参数,比如数据库连接超时时间、文件路径前缀等。这样如果以后要改,只需要改一个地方,不会遗漏。
const 的值必须在编译时就确定,不能依赖运行时输入。
4.5 知识体系图:变量与可变性
下面这张图帮你理清本章的核心逻辑:
4.6 使用场景对比
| 特性 | let(不可变) | let mut(可变) | 变量隐藏(shadowing) | const(常量) |
|---|---|---|---|---|
| 能否修改值 | 否 | 是 | 否(创建新变量) | 否 |
| 能否改变类型 | 否 | 否 | 是 | 否 |
| 作用域 | 块级作用域 | 块级作用域 | 块级作用域 | 全局或块级 |
| 典型场景 | 只读数据、配置值 | 计数器、累加器 | 类型转换、值变换 | 数学常量、全局配置 |
4.7 避坑指南与个人经验
最后,分享几个我踩过的坑:
- 别把 shadowing 当 mut 用:shadowing 是创建新变量,不是修改旧变量。如果你只是想改值,用
mut更清晰。 - 常量名别用错:常量用全大写加下划线,这是 Rust 社区的惯例。我见过有人用驼峰命名,结果被同事吐槽了好久。
- 不要过度 shadowing:在一个函数里反复 shadow 同一个变量名,会让代码很难读懂。我一般只在类型转换时用 shadowing。
好了,这一章的内容就到这里。变量与可变性是 Rust 安全体系的基石,理解它们,你就能写出更健壮的代码。