Rust中的可变借用(&mut T)和不可变借用(&T)区别
在 Rust 中,借用(Borrowing) 是指在不获取所有权(Ownership)的情况下访问数据。
&T(不可变借用)和 &mut T(可变借用)是 Rust 内存安全模型的核心,它们的根本区别在于 权限(读/写) 和 排他性(Exclusivity)。
以下是详细的对比和解释:
1. 核心区别总结
| 特性 | &T (不可变借用) |
&mut T (可变借用) |
|---|---|---|
| 官方术语 | 共享引用 (Shared Reference) | 独占引用 (Exclusive Reference) |
| 读权限 | ✅ 可以读取 | ✅ 可以读取 |
| 写权限 | ❌ 不可修改数据 | ✅ 可以修改数据 |
| 数量限制 | 同一作用域内可以有 无限多个 | 同一作用域内 只能有一个 |
| 共存规则 | 可以与其他 &T 共存 |
不能与其他任何引用(&T 或 &mut T)共存 |
2. 详细解释
A. 不可变借用 (&T)
- 类比:就像PPT演示。所有人(多个引用)都可以同时看屏幕,但没有任何人可以在上面乱涂乱画。
- 规则:你可以创建任意数量的不可变引用。
- 目的:允许数据被并行读取,因为单纯的读取操作不会导致数据竞争。
plaintext
fn main() {
let x = 10;
// 可以同时存在多个不可变借用
let r1 = &x;
let r2 = &x;
println!("r1: {}, r2: {}", r1, r2); // 合法
// *r1 = 20; // ❌ 编译错误:不能通过 &T 修改数据
}
B. 可变借用 (&mut T)
- 类比:就像编辑文档。为了防止冲突,同一时间只能有一个人拥有编辑权限。当你在编辑时,其他人既不能编辑,也不能看(防止看到编辑到一半的中间状态)。
- 规则:在特定作用域内,只能存在一个可变引用,且此时不能存在其他任何引用。
- 目的:防止数据竞争(Data Races)。
plaintext
fn main() {
let mut x = 10;
// 只能有一个可变借用
let r1 = &mut x;
*r1 += 10; // 修改数据
println!("r1: {}", r1); // 合法
}
3. Rust 的“借用规则” (The Borrow Checker Rules)
Rust 编译器通过 借用检查器 强制执行以下铁律:
在任意给定时间,你要么只能拥有一个可变引用,要么拥有任意数量的不可变引用。二者不能同时存在。
❌ 错误示例:同时存在可变和不可变借用
这是新手最常遇到的错误。
plaintext
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 不可变借用
let r2 = &mut s; // ❌ 错误!已经借用为不可变了,不能再借用为可变
println!("{}, {}", r1, r2);
}
为什么禁止?
如果 r2 修改了 s 的内存(比如 push_str 导致扩容重新分配内存),那么 r1 指向的地址就会变成无效内存(悬垂指针),或者 r1 读到的数据是不一致的。
❌ 错误示例:多个可变借用
plaintext
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // ❌ 错误!不能同时有两个可变借用
// println!("{}", r1); // 如果这里使用了 r1,上面的 r2 定义就会报错
}
为什么禁止?
防止数据竞争(Data Race)。如果两个指针同时试图写入同一块内存,且没有同步机制,程序的行为将是未定义的。
4. 作用域与生命周期 (NLL)
Rust 的借用检查非常智能。从 Rust 2018 版本开始引入了 非词法生命周期 (Non-Lexical Lifetimes, NLL)。这意味着引用的有效范围不再是整个花括号 {},而是最后一次使用它的地方。
plaintext
fn main() {
let mut x = 10;
let r1 = &x;
println!("{}", r1);
// r1 的生命周期在这里就结束了,因为它后面不再被使用了
let r2 = &mut x; // ✅ 合法!因为 r1 已经不再活跃
*r2 = 20;
println!("{}", r2);
}
5. 总结
&T(Shared):你是观众。你可以看,别人也可以看,但谁都不能改。&mut T(Exclusive):你是作者。只有你能看,只有你能改,其他人靠边站。
这种机制在编译阶段就消除了数据竞争,是 Rust 实现“无垃圾回收(GC)且内存安全”的关键。