Skip to content

可变引用与借用规则

> 掌握可变引用(&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
fn main() {
    let mut s = String::from("hello");
    let r = &mut s;
    r.push_str(", world");
    println!("{}", s);
}
▶ Run

详细示例

rust
fn main() {
    // 必须声明为 mut
    let mut s = String::from("hello");

    // 创建可变引用
    let r = &mut s;

    // 通过引用修改数据
    r.push_str(", world!");

    println!("{}", s);  // "hello, world!"
}
▶ Run

函数中修改数据

rust
fn 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!");
}
▶ Run

可变引用的限制

rust
// 限制 1:同一作用域内只能有一个可变引用
fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    // let r2 = &mut s;  // ❌ 错误!

    r1.push_str(" world");
    println!("{}", r1);
}
▶ Run

错误信息:

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 here
rust
// 限制 2:不能同时存在可变引用和不可变引用
fn main() {
    let mut s = String::from("hello");

    let r1 = &s;      // 不可变引用
    let r2 = &mut s;  // ❌ 错误!

    println!("{}, {}", r1, r2);
}
▶ Run

错误信息:

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
// 方案 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);
}
▶ Run

借用规则详解

核心规则

┌─────────────────────────────────────────────────────┐
│                  借用规则                            │
├─────────────────────────────────────────────────────┤
│                                                     │
│  规则 1:任意时刻,只能有以下一种情况               │
│         • 一个可变引用                              │
│         • 任意数量的不可变引用                      │
│                                                     │
│  规则 2:引用不能比所有者存活更久                   │
│         (防止悬垂引用)                            │
│                                                     │
└─────────────────────────────────────────────────────┘

允许的组合

rust
fn main() {
    let mut s = String::from("hello");

    // ✅ 允许多个不可变引用
    let r1 = &s;
    let r2 = &s;
    let r3 = &s;

    println!("r1={}, r2={}, r3={}", r1, r2, r3);
}
▶ Run
rust
fn main() {
    let mut s = String::from("hello");

    // ✅ 可变引用作用域结束后,可以创建新的借用
    {
        let mut r1 = &mut s;
        r1.push_str(", world");
    }  // r1 借用结束

    let r2 = &s;  // ✅ 可以再次借用
    println!("{}", r2);
}
▶ Run
rust
fn 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);
}
▶ Run

不允许的组合

rust
// ❌ 同时存在可变和不可变引用
fn main() {
    let mut s = String::from("hello");

    let immutable = &s;
    let mutable = &mut s;  // ❌ 错误

    println!("{}, {}", immutable, mutable);
}
▶ Run
rust
// ❌ 同时存在两个可变引用
fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;  // ❌ 错误

    r1.push_str(" world");
    r2.push_str("!");
}
▶ Run

为什么有这个限制?

防止数据竞争

数据竞争的三个条件:
1. 两个或多个指针同时访问同一数据
2. 至少有一个是写操作
3. 没有同步机制

Rust 的解决方案:
编译时检查借用规则,从源头防止数据竞争!

场景分析

场景 1:两个可变引用同时写入
┌──────────┐     ┌──────────┐
│ 线程 1    │     │ 线程 2    │
│ 写入 A   │     │ 写入 B   │
│   ↓      │     │   ↓      │
│   数据 X │←────┘          │
└──────────┘

结果:数据竞争!数据可能损坏
Rust: 编译错误,只允许一个可变引用


场景 2:读和写同时发生
┌──────────┐     ┌──────────┐
│ 线程 1    │     │ 线程 2    │
│ 读取 A   │     │ 写入 B   │
│   ↓      │     │   ↓      │
│   数据 X │←────┘          │
└──────────┘

结果:数据竞争!可能读到不一致的数据
Rust: 编译错误,不能同时有读和写

小结

  • 可变引用(&mut T)允许修改借用的数据
  • 借用规则:同一时刻只能有多个不可变引用或一个可变引用
  • 可变引用必须独占,防止数据竞争
  • 使用作用域分离或按顺序使用来避免借用冲突

练习题

详见:练习题