变量与常量
在 Rust 中,变量绑定默认是不可变的(immutable)。这是 Rust 安全性保证的基石之一。
变量绑定
使用 let 关键字创建变量绑定:
fn main() {
let x = 5;
println!("x 的值是: {}", x);
}
不可变性(Immutability)
默认情况下,变量是不可变的:
fn main() {
let x = 5;
// x = 6; // ❌ 编译错误:cannot assign twice to immutable variable
println!("x = {}", x);
}
为什么默认不可变?
- 🔒 安全性: 防止意外修改
- 🚀 并发: 多线程环境下更安全
- 🧠 可读性: 代码更容易理解
- ⚡ 优化: 编译器可以做更多优化
可变变量
使用 mut 关键字声明可变变量:
fn main() {
let mut x = 5;
println!("x 的初始值: {}", x);
x = 6; // ✅ 可以修改
println!("x 的新值: {}", x);
}
Loading diagram...
变量遮蔽(Shadowing)
Rust 允许用相同名称声明新变量,"遮蔽"旧变量:
fn main() {
let x = 5;
let x = x + 1; // 遮蔽:创建新变量
{
let x = x * 2; // 内层作用域的遮蔽
println!("内层 x = {}", x); // 12
}
println!("外层 x = {}", x); // 6
}
遮蔽 vs 可变性
遮蔽的优势:
- 可以改变类型:
fn main() {
let spaces = " "; // &str 类型
let spaces = spaces.len(); // ✅ usize 类型,遮蔽允许改变类型
// 对比可变变量(不允许改变类型):
let mut count = " ";
// count = count.len(); // ❌ 编译错误:类型不匹配
}
- 保持不可变性:
fn main() {
let x = 5;
let x = x + 1; // 新的 x 仍然是不可变的
// x = 10; // ❌ 不能修改
}
Loading diagram...
常量(Constants)
常量使用 const 关键字声明,并且必须标注类型:
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159;
fn main() {
println!("最大点数: {}", MAX_POINTS);
}
常量的特点
| 特性 | 常量 | 不可变变量 |
|---|---|---|
| 关键字 | const | let |
| 类型标注 | 必须 | 可选(类型推导) |
| 作用域 | 可以是全局 | 通常是局部 |
| 值 | 编译时确定 | 运行时确定 |
| 表达式 | 只能是常量表达式 | 任意表达式 |
| 命名规范 | SCREAMING_SNAKE_CASE | snake_case |
常量 vs 变量
// ✅ 常量:编译时求值
const SECONDS_IN_HOUR: u32 = 60 * 60;
fn main() {
// ✅ 不可变变量:运行时求值
let runtime_value = get_value();
// ❌ 常量不能使用运行时函数
// const WRONG: u32 = get_value();
}
fn get_value() -> u32 {
42
}
类型推导
Rust 有强大的类型推导能力:
fn main() {
let x = 5; // 推导为 i32
let y = 2.0; // 推导为 f64
let is_true = true; // 推导为 bool
// 显式类型标注
let z: u64 = 100;
// 通过使用方式推导
let mut guess = String::new();
guess.push_str("42");
let guess: i32 = guess.trim().parse().expect("Not a number!");
}
数字分隔符
为了提高可读性,可以在数字中使用下划线:
fn main() {
let million = 1_000_000;
let hex = 0xff_ff_ff;
let binary = 0b1111_0000;
let float = 1_234.567_890;
println!("一百万: {}", million);
}
实践建议
✅ 推荐做法
fn main() {
// 1. 默认使用不可变变量
let price = 100;
// 2. 只在需要时使用 mut
let mut count = 0;
count += 1;
// 3. 常量用于魔法数字
const TAX_RATE: f64 = 0.08;
let total = price as f64 * (1.0 + TAX_RATE);
// 4. 使用遮蔽改变类型
let input = "42";
let input: i32 = input.parse().unwrap();
}
❌ 避免做法
fn main() {
// ❌ 不必要的 mut
let mut x = 5; // 如果后续不修改,不要用 mut
println!("{}", x);
// ❌ 魔法数字(应该用常量)
let tax = price * 0.08; // 0.08 是什么?
// ❌ 过度使用遮蔽(可读性差)
let x = 1;
let x = x + 1;
let x = x * 2;
let x = x - 1;
// 太多遮蔽会让代码难以追踪
}
内存视角
变量绑定在内存中的表示:
fn main() {
let x: i32 = 42;
let y: i32 = x; // Copy trait: 栈上复制
println!("x = {}, y = {}", x, y);
}
栈上的布局:
栈内存:
┌──────────┐
│ x: 42 │ ← i32 在栈上,4 字节
├──────────┤
│ y: 42 │ ← 复制值,不是引用
└──────────┘
练习题
练习 1:修复编译错误
fn main() {
let x = 5;
x = 6; // ❌ 编译错误
println!("{}", x);
}
查看答案
fn main() {
let mut x = 5; // 添加 mut 关键字
x = 6;
println!("{}", x);
}
练习 2:使用遮蔽
将字符串转换为数字,使用遮蔽技术:
fn main() {
let guess = "42";
// 在这里将 guess 转换为数字(提示:使用遮蔽)
}
查看答案
fn main() {
let guess = "42";
let guess: i32 = guess.parse().expect("Not a number!");
println!("数字是: {}", guess);
}
小结
- ✅ Rust 中的变量默认不可变
- ✅ 使用
mut关键字声明可变变量 - ✅ 遮蔽允许重新绑定变量,可以改变类型
- ✅ 常量用
const声明,必须标注类型,编译时求值 - ✅ 不可变性带来安全性、并发性和优化优势
- ✅ 优先使用不可变,只在必要时使用可变
下一步,我们将学习 Rust 的数据类型系统。