可变引用与借用规则
> 掌握可变引用(&mut T)的用法,理解 Rust 借用规则如何防止数据竞争。
可变引用(&mut T)
可变引用语法
概念名称: &mut T 创建可变引用(可读写借用)。
语法结构:
┌──────────────────────────────────────┐
│ let mut 变量 = 值; │
│ let 引用名 = &mut 变量; │
│ ↑ ↑ │
│ 名字 可变借用操作符 │
│ │
│ let mut s = String::from("hi"); │
│ let r = &mut s; → r 可修改 s │
│ r.push_str(" world"); │
└──────────────────────────────────────┘最简示例
rust
▶ Runfn main() {
let mut s = String::from("hello");
let r = &mut s;
r.push_str(", world");
println!("{}", s);
}详细示例
rust
▶ Runfn main() {
// 必须声明为 mut
let mut s = String::from("hello");
// 创建可变引用
let r = &mut s;
// 通过引用修改数据
r.push_str(", world!");
println!("{}", s); // "hello, world!"
}函数中修改数据
rust
▶ Runfn main() {
let mut greeting = String::from("hello");
make_friendly(&mut greeting);
println!("{}", greeting); // "hello, nice to meet you!"
}
fn make_friendly(s: &mut String) {
s.push_str(", nice to meet you!");
}可变引用的限制
rust
▶ Run// 限制 1:同一作用域内只能有一个可变引用
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = &mut s; // ❌ 错误!
r1.push_str(" world");
println!("{}", r1);
}错误信息:
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> src/main.rs:5:14
|
4 | let r1 = &mut s;
| ------ first mutable borrow occurs here
5 | let r2 = &mut s; // ❌ 错误!
| ^^^^^^ second mutable borrow occurs here
6 | r1.push_str(" world");
| --------------------- first borrow later used hererust
▶ Run// 限制 2:不能同时存在可变引用和不可变引用
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 不可变引用
let r2 = &mut s; // ❌ 错误!
println!("{}, {}", r1, r2);
}错误信息:
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:5:14
|
4 | let r1 = &s; // 不可变引用
| -- immutable borrow occurs here
5 | let r2 = &mut s; // ❌ 错误!
| ^^^^^^ mutable borrow occurs here
6 | println!("{}, {}", r1, r2);
| ------ immutable borrow later used here正确的使用方式
rust
▶ Run// 方案 1:分离作用域
fn main() {
let mut s = String::from("hello");
{
let r1 = &mut s;
r1.push_str(", world");
} // r1 作用域结束,借用结束
let r2 = &s; // ✅ 现在可以了
println!("{}", r2);
}
// 方案 2:按顺序使用
fn main() {
let mut s = String::from("hello");
let r1 = &s;
println!("{}", r1); // 使用完 r1
let r2 = &mut s; // ✅ 现在可以借用了
r2.push_str(" world");
println!("{}", r2);
}借用规则详解
核心规则
┌─────────────────────────────────────────────────────┐
│ 借用规则 │
├─────────────────────────────────────────────────────┤
│ │
│ 规则 1:任意时刻,只能有以下一种情况 │
│ • 一个可变引用 │
│ • 任意数量的不可变引用 │
│ │
│ 规则 2:引用不能比所有者存活更久 │
│ (防止悬垂引用) │
│ │
└─────────────────────────────────────────────────────┘允许的组合
rust
▶ Runfn main() {
let mut s = String::from("hello");
// ✅ 允许多个不可变引用
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("r1={}, r2={}, r3={}", r1, r2, r3);
}rust
▶ Runfn main() {
let mut s = String::from("hello");
// ✅ 可变引用作用域结束后,可以创建新的借用
{
let mut r1 = &mut s;
r1.push_str(", world");
} // r1 借用结束
let r2 = &s; // ✅ 可以再次借用
println!("{}", r2);
}rust
▶ Runfn main() {
let mut s = String::from("hello");
// ✅ 可变借用结束后,可以再次可变借用
{
let r1 = &mut s;
r1.push_str(", world");
} // r1 结束
{
let r2 = &mut s;
r2.push_str("!");
} // r2 结束
let r3 = &s; // ✅ 可以不可变借用
println!("{}", r3);
}不允许的组合
rust
▶ Run// ❌ 同时存在可变和不可变引用
fn main() {
let mut s = String::from("hello");
let immutable = &s;
let mutable = &mut s; // ❌ 错误
println!("{}, {}", immutable, mutable);
}rust
▶ Run// ❌ 同时存在两个可变引用
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // ❌ 错误
r1.push_str(" world");
r2.push_str("!");
}为什么有这个限制?
防止数据竞争
数据竞争的三个条件:
1. 两个或多个指针同时访问同一数据
2. 至少有一个是写操作
3. 没有同步机制
Rust 的解决方案:
编译时检查借用规则,从源头防止数据竞争!场景分析
场景 1:两个可变引用同时写入
┌──────────┐ ┌──────────┐
│ 线程 1 │ │ 线程 2 │
│ 写入 A │ │ 写入 B │
│ ↓ │ │ ↓ │
│ 数据 X │←────┘ │
└──────────┘
结果:数据竞争!数据可能损坏
Rust: 编译错误,只允许一个可变引用
场景 2:读和写同时发生
┌──────────┐ ┌──────────┐
│ 线程 1 │ │ 线程 2 │
│ 读取 A │ │ 写入 B │
│ ↓ │ │ ↓ │
│ 数据 X │←────┘ │
└──────────┘
结果:数据竞争!可能读到不一致的数据
Rust: 编译错误,不能同时有读和写小结
- 可变引用(
&mut T)允许修改借用的数据 - 借用规则:同一时刻只能有多个不可变引用或一个可变引用
- 可变引用必须独占,防止数据竞争
- 使用作用域分离或按顺序使用来避免借用冲突
练习题
详见:练习题