首页 / Rust 入门教程 / 常量与 Shadowing

Rust 入门教程

常量与 Shadowing

本教程共 78 篇 · 第 8 篇 · 更新于 2026-08-08 · 约 7 分钟阅读

RustRust 入门教程const常量shadowing重影let

本节目标:分清 const 常量和不可变变量的差别,并掌握 shadowing(同名重绑定)这个实用技巧,避免和 mut 搞混。

上一章说变量默认不可变,有人就问:那不就是常量吗?不是。Rust 里”常量”是另一个东西,用 const 声明;另外还有一种叫 shadowing 的玩法,容易和”改值”混淆。这一章一次讲清。

8-1 声明常量用 const

常量长这样:

const MAX_POINTS: u32 = 100_000;

规矩比变量严得多:

  • const 而不是 let
  • 必须标注类型,不能靠推断。
  • 值必须能在编译期就算出来,不能是运行时才能确定的结果(比如不能调用一个会读文件的函数的返回值)。
  • 命名惯例是全大写加下划线。
Note

100_000 里的下划线是 Rust 允许的”数字分隔符”,纯粹为了好看,编译器当它是 100000。大数字这么写一眼就读得清。

8-2 常量和不可变变量的三大区别

别看”不可变变量”也改不了,它和常量不是一回事:

  1. 声明关键字不同:变量 let,常量 const
  2. 作用域位置不同:常量可声明在任意作用域,包括全局(函数外面);而 let 变量一般声明在函数内。常量常驻整个程序生命周期。
  3. 值的确定时机不同:常量的值在编译期就固定死;不可变变量的值虽然后来不能改,但它是运行时才绑定的。

一句话:常量是”编译期写死的全局定值”,变量是”运行期绑定但此后不改的局部值”。

Tip

经验法则:一个值在整个程序里都该是同一个固定数(比如圆周率、最大连接数),用 const;只是某段逻辑里暂时不改的,用 let

8-3 什么是 Shadowing

shadowing 翻译叫”重影”或”遮蔽”。意思是:你可以用同一个名字再声明一次 let,后面的会”盖住”前面的:

fn main() {
    let x = 5;
    let x = x + 1;   // 新 x 遮蔽旧 x,值是 6
    let x = x * 2;   // 再遮蔽,值是 12
    println!("x 最终是 {x}");
}

输出 x 最终是 12。这里每次都是全新的 let x,不是给老 x 赋值。旧 x 在它被遮蔽后就看不见了,但内存上它已被新绑定取代。

8-4 Shadowing 能改变类型

shadowing 最妙的地方:可以在”重绑”时换类型,这是 mut 做不到的:

fn main() {
    let spaces = "   ";      // 这是字符串
    let spaces = spaces.len(); // 重绑成数字(空格个数)
    println!("空格数是 {spaces}");
}

第一个 spaces 是字符串,第二个 spaces 是整数。如果用 mutlet mut spaces = " "; spaces = spaces.len(); 会报错,因为类型不能变。shadowing 绕过了这个限制,因为它本质上是”新声明”,不是”改旧值”。

Warning

别把 shadowing 和 mut 混为一谈。 mut 是同一个变量改值、类型不变;shadowing 是同名的新变量把旧变量遮住,连类型都能换。初学者常以为 let x = x + 1 是给 x 加一,其实它是拿旧 x 算出新值再绑回 x。

8-5 shadowing 的典型用途

最常见的场景是”先拿到一个原始输入,再加工成最终形态”。比如读取用户输入是字符串,你用 shadowing 把它转成整数:

let input = "42";        // 字符串
let input: u32 = input.trim().parse().unwrap(); // 转成整数,类型也换了

虽然名字没变,语义上完成了”从文本到数字”的转换。代码读起来顺,名字还不用绞尽脑汁另起。

8-6 作用域内的遮蔽范围

shadowing 只在它所在的作用域及其内层生效。离开作用域,旧值就回来了——但真正理解作用域要到后面章节,这里先知道”遮蔽有范围”即可,不必钻牛角尖。

8-7 三者怎么选

  • 值固定不变、全局通用 → const
  • 局部值、编完后不改 → let(默认不可变)。
  • 局部值、确实需要改 → let mut
  • 想在同名下做”类型转换/加工重绑” → shadowing(再写一次 let)。
Tip

新手容易滥用 mut。其实很多”改值”需求,用 shadowing 表达更清晰,也更符合 Rust 鼓励不可变的习惯。多想想能不能用 shadowing 代替 mut

8-8 常量与 shadowing 的搭配技巧

常量配合 shadowing 有个常见写法:先用 const 定义全局基准,再用 shadowing 在局部做换算。比如游戏里先 const MAX_HP: u32 = 100;,某函数里 let max_hp = MAX_HP / 2; 表示”当前关卡半血上限”,名字复用、语义清晰,且没引入 mut

不过要小心别过度 shadowing。如果一个名字在函数里被重绑四五次,读代码的人要不停回想”现在的 x 到底是第几个 x”。经验是:shadowing 两三次以内没问题;再多就说明该换个有意义的变量名,或者把逻辑拆成小函数了。

Note

shadowing 产生的是全新绑定,旧绑定被遮蔽后不能再被引用,编译器保证你不会误用到”已经被盖住”的那个值。它和生命周期有微妙关系,但那属于进阶话题,现阶段先掌握”同名重绑、可换类型”即可。

8-9 常量命名与误区

常量命名有个强约定:全大写加下划线,比如 MAX_POINTSPIDEFAULT_TIMEOUT。这和其他语言的常量风格一致,一眼就能认出”这是个编译期定值”。

一个常见误区是拿 const 当”全局可变变量”用——不行。const 绝不能加 mut,它从定义到程序结束都是固定值。如果你需要”一个全局、运行期会变的量”,那超出了入门范围(涉及 static 与内部可变性),现阶段记住 const 就是死值即可。

Warning

别在 const 里写依赖运行时输入的表达式(比如 const X: i32 = read_number();),编译器会拒绝,因为常量必须在编译期就能算出来。常量只放”写死在代码里的数字/字符串”。需要运行期确定的,用 let 变量。

8-10 shadowing 的边界

shadowing 虽好,也有被滥用的边界。如果一个变量名在函数里被反复重绑四五次,读代码的人要不停回想”现在的 x 到底是第几个 x、什么类型”,认知负担会很重。经验法则:shadowing 两三次以内是清爽的;再多,就该给中间结果起不同的、有含义的名字,或把逻辑拆成小函数。

Warning

另一个隐患:shadowing 会让调试时”变量的值”变得依赖”执行到哪一行”,因为你同一个名字在不同时刻指向不同值。如果只是想改值,用 let mut 往往比连续 shadowing 更容易在调试器里跟踪。shadowing 最适合”类型转换”这类语义清晰的场景,别把它当通用赋值用。

8-11 小结

常量 const 是编译期写死的全局定值;shadowing 是用同名 let 重新绑定、连类型都能换。它俩和 mut 各管一摊。下一章进入”数据类型”,我们先看标量类型里的整数和浮点数。