讲解

Rust 的变量用 let 声明,并且默认不可变(immutable)——这是 Rust 与大多数语言相反的设计。不可变是默认项,意味着每当你看到 let x = 5,就可以确信 x 在这段作用域里永远是 5,阅读代码时心智负担小得多;想修改必须显式加 mut(mutable),让「这里会变」成为代码里显眼的标记。

试图修改不可变变量不是风格问题,而是编译错误。编译器这一拦看着烦,实则把一整类「某个值在别处被悄悄改掉」的 bug 消灭在编译期。多线程场景下这个约束的价值会成倍放大(第 24 章)。

遮蔽(shadowing)是 Rust 特有的能力:用 let 再次声明同名变量,前一个变量被「遮蔽」,之后的代码看到的是新变量。它和 mut 的区别很关键——遮蔽是创建新变量,可以改变类型;mut 是原地修改,类型不能变。对「同一个逻辑值的多步变换」(比如字符串去空格再转数字),遮蔽既安全又顺读。

最后是常量 const:必须用 const 关键字、必须标注类型、只能赋编译期可求值的表达式、全大写加下划线命名(SCREAMING_SNAKE_CASE)。常量在所有作用域都可声明,且永远不可变——连 mut 都不行。

示例

不可变是默认值,可变要显式声明:

fn main() {
    let x = 5;
    println!("x = {x}");

    let mut y = 10;
    println!("y 初始为 {y}");
    y = 20;
    println!("y 现在是 {y}");

    assert_eq!(x, 5);
    assert_eq!(y, 20);
}

如果试图修改不可变变量,编译会直接失败(本教程的构建脚本会验证这段代码确实编译不过):

// compile-fail: 修改不可变变量
fn main() {
    let x = 5;
    x = 6;
    println!("{x}");
}

编译器给出的报错大意如下,注意它连修复建议都写好了:

error[E0384]: cannot assign twice to immutable variable `x`
 --> main.rs:3:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
  |         help: consider making this binding mutable: `mut x`
3 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

遮蔽可以连类型一起换;常量则恒定不变:

const MAX_PLAYERS: u32 = 100_000;

fn main() {
    let spaces = "   ";
    let spaces = spaces.len(); // 遮蔽:从 &str 变成 usize

    let mut count = spaces;
    count += 1; // mut 只能改值,类型还是 usize

    println!("空格数 {spaces},加一后 {count},上限 {MAX_PLAYERS}");
    assert_eq!(count, 4);
}

常见坑

  • 满屏 mut:把每个变量都标 mut 等于关掉了编译器的保护,还会收到 unused mut 警告。只在真的需要修改时加。
  • 分不清遮蔽和 mut:let 重复声明是遮蔽(新变量、可换类型);不带 let 的赋值是修改(要求原变量是 mut、类型不变)。
  • 给 const 赋运行时值:const 的值必须在编译期确定,函数调用结果、用户输入都不行——那种场景用 let。
  • 警告当错误忽视:编译器对未使用变量的警告(unused variable)往往指向逻辑漏洞——本来想用的值没用上,先看警告再继续。

小结

let 默认不可变,mut 显式开修改权限;遮蔽用同名新变量做变换;const 是编译期常量。这些约定让「值在哪里会变」一目了然。下一章认识标量类型。