Rust 变量可变性、遮蔽与 const/static
更新时间:2026-09-20。Rust 变量默认不可变,这不是语法糖,而是所有权体系的一部分。所有权与移动语义见 所有权与借用规则。
一、let 与 mut
rust
let x = 5;
// x = 6; // 编译错误:cannot assign twice to immutable variable
let mut y = 5;
y = 6; // OK:绑定声明为 mut| 写法 | 含义 | 关键点 |
|---|---|---|
let x = v | 不可变绑定 | 不能通过 x 改值,但变量可被 move |
let mut x = v | 可变绑定 | 才能对 x 赋值、调用 &mut 方法 |
let mut s = &v | 可变的引用变量 | 引用本身可换指向,但不能改值 |
let s = &mut v | 持有可变引用 | 能改值,但 s 这个绑定不可换指向 |
「可变绑定」说的是变量名这个入口能不能改,和值有没有被 &mut 借用是两件事。
二、遮蔽(shadowing)不是重新赋值
rust
let spaces = " "; // &str
let spaces = spaces.len(); // 用 let 再次绑定,类型都能变:usize- 遮蔽是新建一个同名变量覆盖旧的,旧变量被藏起来(仍可能因 move/借用受所有权约束),允许类型变化。
- 直接
spaces = spaces.len()是非法的:不可变绑定不能赋值,且类型也不允许变。
rust
let mut n = 1;
n = 2; // 同一变量、同类型重新赋值,需要 mut
let n = "two"; // 遮蔽:新变量,可换类型,不要求 mut三、const 与 static
rust
const MAX_POINTS: u32 = 100_000; // 编译期常量,内联到使用处,无固定地址
static BANNER: &str = "ready"; // 静态变量,贯穿整个程序,有固定内存地址
static mut COUNTER: u32 = 0; // 可变静态:访问需 unsafe,多线程是雷区let 绑定 | const | static | |
|---|---|---|---|
| 标注类型 | 可推断 | 必须显式 | 必须显式 |
| 内存 | 栈上绑定 | 编译期内联,无地址 | 固定地址,'static 寿命 |
| 可变性 | mut | 恒不可变 | 可 static mut,但需 unsafe |
| 初始化 | 可为运行时值 | 只能常量表达式 | 只能常量表达式 |
const 适合魔法数字/上限;static 适合全局表、字符串。可变全局优先 AtomicXxx、Mutex/OnceLock,不要直接用 static mut(多线程访问是未定义行为,亦见 unsafe 边界)。
四、常见坑
- 漏写 mut:报错指向赋值处而非声明处,回声明加
let mut即可;但要先想清楚是「需要可变性」还是「该用新绑定」。 - 把遮蔽当可变用:
let x = ...重复声明能用,但在循环累加器上应真正let mut x,否则每次都是新变量、累不起来。 - 混淆可变绑定与
&mut:fn f(v: Vec<u8>)想改元素得声明mut v;想让调用方看到改动要传&mut v。 const放需要地址的值:要取地址/做全局单例时用static,const每次使用都是一份内联副本。
总结
Rust 的 let 绑定默认不可变、要修改必须显式 mut,且「绑定可变」(变量入口能赋值)与「可变引用 &mut」(能改借用的值)是两回事;用 let 重复声明做的遮蔽是新建同名变量、允许换类型,区别于同类型重新赋值;const 是必须显式类型、编译期内联无地址的常量,static 是有固定地址、贯穿 'static 寿命的全局,可变全局不要用 static mut 硬来,而应交给原子类型或 Mutex/OnceLock。