第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;

常量有几个特点:

  • 必须显式标注类型(比如 u32f64)。
  • 命名惯例是全大写加下划线。
  • 可以在任何作用域声明,包括全局作用域。
  • 值必须是编译期常量表达式,不能是运行时计算的结果。

我在项目中常用常量来定义配置参数,比如数据库连接超时时间、文件路径前缀等。这样如果以后要改,只需要改一个地方,不会遗漏。

避坑指南:我曾经把常量用在需要动态计算的地方,结果编译报错。记住,const 的值必须在编译时就确定,不能依赖运行时输入。

4.5 知识体系图:变量与可变性

下面这张图帮你理清本章的核心逻辑:

变量声明 let(不可变) let mut(可变) const(常量) 默认安全,不能二次赋值 可修改值,类型不能变 编译期确定,全局可用 变量隐藏(Shadowing) 核心原则:默认不可变,显式声明可变

4.6 使用场景对比

特性 let(不可变) let mut(可变) 变量隐藏(shadowing) const(常量)
能否修改值 否(创建新变量)
能否改变类型
作用域 块级作用域 块级作用域 块级作用域 全局或块级
典型场景 只读数据、配置值 计数器、累加器 类型转换、值变换 数学常量、全局配置

4.7 避坑指南与个人经验

最后,分享几个我踩过的坑:

  • 别把 shadowing 当 mut 用:shadowing 是创建新变量,不是修改旧变量。如果你只是想改值,用 mut 更清晰。
  • 常量名别用错:常量用全大写加下划线,这是 Rust 社区的惯例。我见过有人用驼峰命名,结果被同事吐槽了好久。
  • 不要过度 shadowing:在一个函数里反复 shadow 同一个变量名,会让代码很难读懂。我一般只在类型转换时用 shadowing。

好了,这一章的内容就到这里。变量与可变性是 Rust 安全体系的基石,理解它们,你就能写出更健壮的代码。


专注资料整理