2.1 变量与常量
在任何编程语言中,变量和常量都是最基础的概念。它们用于存储和表示数据。Rust 在变量和常量的设计上,引入了一些独特且强大的特性,尤其是不可变性(Immutability) 的默认原则,这从根本上影响了代码的安全性和可读性。
变量(Variables)
在 Rust 中,使用 let 关键字来声明一个变量。
let x = 5;
默认情况下,Rust 中的变量是不可变的(immutable)。这意味着一旦一个值被绑定到一个变量名上,你就无法再改变这个值。尝试修改一个不可变变量会导致编译错误。
let x = 5;
x = 6; // 错误!不能对不可变变量 x 进行二次赋值
这种设计是 Rust 的核心安全特性之一。它强制开发者明确地声明哪些值是可以变化的,从而避免了许多因为意外修改而导致的 bug。编译器会在编译时检查这些规则,确保数据流的安全性。
如果你需要一个可以改变的变量,可以使用 mut 关键字显式地声明它为可变的(mutable)。
let mut y = 10;
println!("y 的初始值是: {}", y);
y = 20; // 正确,因为 y 被声明为可变的
println!("y 的新值是: {}", y);
变量遮蔽(Shadowing)
Rust 允许你声明一个与之前变量同名的新变量。这被称为“遮蔽”。新变量会“遮蔽”掉之前的变量,直到它自身的作用域结束或被再次遮蔽。
let z = 5;
let z = z + 1; // 这里我们声明了一个新的变量 z,它遮蔽了之前的 z
{
let z = z * 2; // 在这个内部作用域中,z 被另一个新的 z 遮蔽
println!("内部作用域中的 z 是: {}", z); // 输出: 12
}
println!("外部作用域中的 z 是: {}", z); // 输出: 6,内部作用域的遮蔽已经结束
遮蔽与 mut 不同:
mut是在同一个变量上修改其值。- 遮蔽 是创建一个全新的变量。它允许我们改变一个变量的类型,或者改变其可变性,但复用同一个变量名。
let spaces = " "; // spaces 是 &str 类型
let spaces = spaces.len(); // 遮蔽后,spaces 变成了 usize 类型
常量(Constants)
常量是绑定到一个名称且永远不允许改变的值。常量与不可变变量有几个关键区别:
- 声明关键字: 常量使用
const关键字声明,而不是let。 - 类型必须注明: 常量的类型必须显式标注。变量在很多情况下可以由编译器推断,但常量不行。
- 常量可以是任何作用域: 常量可以在任何作用域中声明,包括全局作用域。这使得它们非常适合用于程序中多处需要的、硬编码的值。
- 常量只能被设置为常量表达式: 常量不能被设置为一个只能在运行时计算出的值。它们必须在编译时就能确定。
const MAX_POINTS: u32 = 100_000; // 使用下划线增强可读性,类型必须为 u32
fn main() {
println!("最大点数是: {}", MAX_POINTS);
// const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3; // 这也是一个常量表达式,可以在编译时计算
}
Rust 中常量的命名约定是全部使用大写字母,单词之间用下划线分隔。
总结
| 特性 | let (默认) | let mut | const |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 不可变 |
| 类型标注 | 通常可以推断 | 通常可以推断 | 必须标注 |
| 作用域 | 局部作用域 | 局部作用域 | 任何作用域(包括全局) |
| 值 | 任何表达式 | 任何表达式 | 编译时常量表达式 |
| 关键字 | let | let mut | const |
理解变量与常量的区别,以及 Rust 默认不可变的哲学,是写出安全、高效 Rust 代码的第一步。在后续的章节中,你会看到这一设计原则如何贯穿于所有权、借用等更高级的概念中。
