Rust开发中变量与常量的定义及使用(let/const关键字实践)
案例4: 变量与常量的定义及使用(let/const关键字实践)
本文是《Rust开发入门实战案例100例》中的第4个案例,深入讲解Rust中变量与常量的定义方式、作用域规则、可变性控制以及实际编程中的最佳实践。通过本案例的学习,读者将掌握
let和const关键字的核心用法,理解 Rust 在内存安全和编译期检查方面的设计理念,并能够熟练运用这些基础知识构建可靠的程序结构。
一、变量与常量:Rust 编程的基石
在任何编程语言中,变量和常量都是存储数据的基本手段。Rust 作为一门注重安全性与性能的系统级语言,在变量和常量的设计上体现了其独特的理念:默认不可变、显式声明可变、编译时严格检查。
与其他语言(如 Python 或 JavaScript)不同,Rust 中的变量默认是不可变的(immutable)。这意味着一旦一个值被绑定到变量名上,就不能再修改它——除非你明确地声明它是可变的。
这种设计有助于防止意外的数据更改,提升程序的安全性和可维护性。
1. 使用 let 定义变量
在 Rust 中,使用 let 关键字来声明变量:
let x = 5;
这行代码将值 5 绑定到变量 x 上。此时 x 是不可变的,如果你尝试修改它:
x = 6; // ❌ 编译错误!
编译器会报错:
error[E0384]: cannot assign twice to immutable variable `x`
要让变量可变,必须使用 mut 关键字:
let mut y = 5;
y = 6; // ✅ 合法,因为 y 被声明为可变
println!("y 的值是 {}", y); // 输出:y 的值是 6
2. 使用 const 定义常量
常量使用 const 关键字声明,且必须指定类型:
const MAX_POINTS: u32 = 100_000;
注意:
- 常量名通常采用全大写加下划线格式(SCREAMING_SNAKE_CASE)
- 类型标注是必需的(不能省略)
- 只能用于表达式在编译时就能计算出结果的情况
- 常量可以在任意作用域内声明,包括全局作用域
常量在整个程序生命周期中都有效,且不允许重新赋值。
二、代码演示:从基础到进阶实践
下面我们通过一系列递进式的代码示例,展示 let 和 const 的各种用法。
示例 1:基本变量声明与可变性控制
fn main() {
let a = 10;
let mut b = 20;
println!("a = {}, b = {}", a, b);
// a = 15; // ❌ 错误:无法修改不可变变量
b = 25; // ✅ 正确:b 是可变的
println!("修改后:a = {}, b = {}", a, b);
}
输出:
a = 10, b = 20
修改后:a = 10, b = 25
⚠️ 提示:即使你不打算改变变量,也不要滥用
mut。保持变量不可变是一种良好的编程习惯,有助于减少副作用和逻辑错误。
示例 2:变量遮蔽(Shadowing)
Rust 支持“变量遮蔽”机制——即使用相同的名称重新声明变量,新的变量会“遮蔽”旧的变量:
fn main() {
let x = "hello";
let x = x.len(); // 遮蔽前一个 x,类型从 &str 变为 usize
let x = x * 2; // 再次遮蔽,x 变为整数并乘以 2
println!("x = {}", x); // 输出:x = 10
}
✅ 优点:
- 允许在不引入新变量名的情况下转换数据类型或处理中间结果
- 比可变变量更安全,因为每次遮蔽都会创建一个新的绑定,原值不会被修改
❌ 注意:只能用 let 进行遮蔽,不能对 const 或静态变量进行遮蔽。
示例 3:常量的跨作用域使用
// 全局常量
const PI: f64 = 3.14159265359;
const APP_NAME: &str = "MyRustApp";
fn main() {
println!("应用名称:{}", APP_NAME);
println!("圆周率近似值:{}", PI);
let radius = 5.0;
let area = PI * radius * radius;
println!("半径为 {} 的圆面积是 {:.2}", radius, area);
}
输出:
应用名称:MyRustApp
圆周率近似值:3.14159265359
半径为 5 的圆面积是 78.54
📌 建议:将程序中频繁使用的固定值(如配置上限、协议版本号、数学常数等)定义为
const,提高可读性和维护性。
示例 4:结合模式解构进行变量声明
Rust 的 let 不仅能绑定单一值,还能配合模式匹配解构复合类型:
fn main() {
// 解构元组
let (name, age) = ("Alice", 25);
println!("{name} 年龄是 {age}");
// 解构数组的部分元素
let numbers = [1, 2, 3, 4, 5];
let [first, second, ..] = numbers;
println!("前两个数字:{}, {}", first, second);
// 忽略某些字段
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 10, y: 20 };
let Point { x, .. } = point;
println!("x 坐标是 {}", x);
}
输出:
Alice 年龄是 25
前两个数字:1, 2
x 坐标是 10
这是 Rust 强大模式匹配能力的体现,也是函数参数、循环遍历中常见的写法。
三、数据表格:变量与常量特性对比
| 特性 | let 变量 |
const 常量 |
|---|---|---|
| 声明关键字 | let / let mut |
const |
| 是否可变 | 默认不可变,可用 mut 改变 |
❌ 永远不可变 |
| 类型标注 | 可选(编译器自动推导) | ✅ 必须显式标注 |
| 作用域 | 局部作用域或块作用域 | 任意作用域(含全局) |
| 初始化时机 | 运行时或编译时 | ✅ 必须在编译时确定 |
| 能否遮蔽 | ✅ 可以多次 let 同名变量 |
❌ 不支持遮蔽 |
| 内存位置 | 栈或堆(取决于类型) | 编译时常量嵌入二进制 |
| 典型用途 | 存储临时数据、状态变化 | 数学常数、配置阈值、协议标识 |
💡 小贴士:
const更适合用于“永远不会变”的值;而let mut应谨慎使用,优先考虑不可变性 + 遮蔽的方式重构逻辑。
四、关键字高亮说明
在上述代码中,以下关键字具有特殊语义,需重点关注其语法行为:
let:变量绑定关键字,用于将值绑定到标识符。mut:可变性修饰符,紧跟在let后表示该变量允许后续修改。const:常量声明关键字,用于定义编译期常量。::类型标注操作符,用于指定变量或常量的类型。=:绑定/赋值操作符,左侧为变量名,右侧为表达式。
例如:
let mut count: u32 = 0;
// ↑ ↑ ↑ ↑
// │ │ │ └─ 初始值(运行时表达式)
// │ │ └─────── 类型标注(u32 表示无符号32位整数)
// │ └───────────── 可变性声明
// └────────────────── 变量绑定关键字
这些关键字共同构成了 Rust 中数据声明的基础语法体系。
五、分阶段学习路径
为了帮助初学者系统掌握变量与常量的使用,我们推荐以下五个阶段的学习路径:
🔹 阶段一:理解不可变性(第1周)
目标:建立“默认不可变”的思维习惯。
✅ 实践任务:
- 编写多个只使用
let(无mut)的程序 - 故意尝试修改不可变变量,观察编译错误信息
- 使用遮蔽代替可变变量完成简单计算链
📌 推荐练习:
let seconds_per_minute = 60;
let minutes_per_hour = 60;
let hours_per_day = 24;
let seconds_per_day = seconds_per_minute * minutes_per_hour * hours_per_day;
println!("{}", seconds_per_day);
🔹 阶段二:掌握 mut 与遮蔽的区别(第2周)
目标:理解何时使用 mut,何时使用遮蔽更合适。
✅ 实践任务:
- 对比两种方式实现字符串长度处理:
- 方式A:
let mut s = String::new(); s.push_str("hello"); - 方式B:
let s = "hello"; let s = s.to_uppercase();
- 方式A:
- 分析各自的优缺点(安全性 vs 灵活性)
📌 提示:优先选择遮蔽,尤其是在类型转换或数据清洗场景中。
🔹 阶段三:学会使用 const 提升代码质量(第3周)
目标:识别项目中可以提取为常量的值。
✅ 实践任务:
- 创建一个小型工具程序(如单位换算器),将所有固定系数定义为
const - 将日志级别、API 地址、超时时间等配置项设为常量
📌 示例:
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
🔹 阶段四:深入理解作用域与生命周期初步概念(第4周)
目标:了解变量的作用域规则,为后续学习打基础。
✅ 实践任务:
- 在
{}块中声明变量,观察超出作用域后的不可访问性 - 尝试在 if/else 分支中声明同名变量,体验遮蔽效果
📌 示例:
let x = 10;
{
let x = "inner";
println!("{}", x); // inner
}
println!("{}", x); // 10(外部 x 未受影响)
🔹 阶段五:综合应用与代码审查(第5周)
目标:写出符合 Rust 风格的高质量代码。
✅ 实践任务:
- 审查已有代码,替换不必要的
mut为遮蔽 - 使用
clippy工具检查变量命名、冗余可变性等问题 - 编写单元测试验证常量正确性
📌 推荐命令:
cargo clippy -- -D clippy::pedantic
六、章节总结
本案例围绕 案例4:变量与常量的定义及使用 展开,全面介绍了 Rust 中 let 和 const 的核心语法与工程实践。以下是关键知识点回顾:
✅ 主要收获
-
变量默认不可变
Rust 的设计哲学强调安全性,因此let x = 5;创建的是不可变绑定。若需修改,必须显式使用let mut x。 -
const用于编译期常量
所有const必须带有类型标注,且值必须在编译时可知。适用于数学常数、配置上限等不变值。 -
变量遮蔽是强大工具
允许重复使用let重新绑定同名变量,可用于类型转换或中间计算,比可变变量更安全。 -
模式解构增强表达力
let支持从元组、结构体、数组中提取字段,简化数据访问流程。 -
命名规范与代码风格
- 变量使用
snake_case - 常量使用
SCREAMING_SNAKE_CASE - 避免过度使用
mut
- 变量使用
-
工具辅助提升质量
使用rustfmt自动格式化代码,clippy检测潜在问题,确保代码符合社区最佳实践。
🛠 常见陷阱与解决方案
| 问题 | 错误示例 | 正确做法 |
|---|---|---|
忘记 mut 导致无法修改 |
let x = 0; x = 1; |
let mut x = 0; x = 1; |
| 常量缺少类型标注 | const MAX: 100; |
const MAX: i32 = 100; |
误以为 const 可在运行时赋值 |
const NOW: u64 = time::now(); |
❌ 不合法,应使用 static 或函数 |
滥用 mut 导致副作用 |
多处随意修改同一变量 | 改用遮蔽或函数式风格 |
🚀 下一步建议
完成本案例后,建议继续学习:
- 案例5:基本数据类型操作 —— 掌握整数、浮点数、布尔值等底层类型的使用
- 案例8:字符串类型(String 与 &str) —— 深入理解 Rust 中字符串的所有权模型
- 案例16:所有权转移机制 —— 为理解复杂数据结构做好准备
同时推荐阅读官方文档章节:
通过本案例的学习,你已经掌握了 Rust 中最基础但最重要的概念之一——如何安全、高效地管理数据的存储与变更。记住:“默认不可变”不是限制,而是一种保护。正是这种严谨的设计,使得 Rust 能够在没有垃圾回收的前提下,依然保证内存安全与并发安全。
接下来,请继续保持动手实践的习惯,逐步构建属于你的 Rust 知识体系。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)