第二章(下):借用 (Borrowing) —— 省钱又省力的 "引用"

Unknown Author 独酌
2025-11-17 Rust, Book

这就引出了我们的下一个痛点:

如果我只是想让你看一眼我的礼物(读取数据),而不是送给你(移交所有权),也没必要专门去复印一份(Clone),该怎么办?

在 Rust 里,这叫 借用 (Borrowing)。 我们会用到一个符号:&

前端视角的类比

在 JavaScript 里,当你把对象传给函数时,默认就是传引用(你可以修改原对象)。 但在 Rust 里:

  1. 直接传值 = 移交所有权 (Move) (变量这之后就废了)。
  2. 传 & = 借用 (Borrow) (只是借你看,看完还我)。

来看看这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
let my_gift = String::from("iPhone");

// ❌ 错误做法:直接传值
// show_gift(my_gift);
// 运行完上一行,my_gift 的所有权就被 show_gift 函数拿走了!
// 这里再想用 my_gift 就会报错。

// ✅ 正确做法:传引用(借给它)
show_gift(&my_gift);

// 因为只是"借"出去,现在我还能接着用
println!("太好了,{} 还在我手里!", my_gift);
}

// 注意函数的参数类型,多了一个 &
fn show_gift(item: &String) {
println!("我看了一眼,这是:{}", item);
}

发现了吗?我们只做了两个微小的改动:

  1. 传参时加了 &:&my_gift
  2. 接收类型加了 &:&String

🧠 随堂小测验

借用虽然好,但别忘了 Rust 的第一章:默认不可变。 当你使用 &my_gift 借出数据时,默认是只读的(Read-only)。借书的人不能在书上乱涂乱画。

假设我想写一个函数,不仅要借用变量,还要修改它(比如给字符串后面追加内容)。

结合我们在第一章学到的知识(关于如何让变量可变),你觉得应该怎么组合这两个概念?

猜猜看: 如果 &String 是“不可变引用”,那么 “可变引用” (Mutable Reference) 应该怎么写?

A. mut &String B. &mut String C. &String mut

🎉 答案就是 B: &mut String

你现在的 Rust 语感已经很好了。这一章我们把最难啃的骨头啃下来了,为了巩固一下,我们来看一个完整的“可变借用”例子。

可变借用实战

这里有一个常见的误区:要想借出去修改,首先原主自己得有修改权。

  1. 变量本身必须是 mut 的。
  2. 传递的时候要传 &mut
  3. 接收的时候要接 &mut
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn main() {
// 1. 变量本身必须是 mut
let mut my_string = String::from("Hello");

// 2. 传参时使用 &mut
add_world(&mut my_string);

println!("修改后的字符串: {}", my_string); // 输出: Hello, World
}

// 3. 函数签名接收 &mut String
fn add_world(s: &mut String) {
s.push_str(", World"); // 修改了借来的数据
}

⚠️ 借用规则的“高压线” (The Golden Rules)

虽然 &mut 很强大,但 Rust 编译器是一个极其严格的管理员。为了防止数据竞争 (Data Race)(也就是前端常遇到的:我在渲染列表,结果后台把列表清空了,导致页面白屏崩溃),Rust 定下了两条铁律:

在任意时刻,针对同一个数据,你只能满足以下二者之一:

  1. 拥有 任意数量 的不可变引用 (&T)
  2. 拥有 仅仅一个 可变引用 (&mut T)

❌ 绝对不能同时存在!

1
2
3
4
5
6
7
8
9
let mut s = String::from("hello");

let r1 = &s; // ✅ OK
let r2 = &s; // ✅ OK
let r3 = &mut s; // ❌ 报错!!!!!

// 报错原因:r1 和 r2还在用呢(只读),你这边 r3 居然要去改它?
// 万一 r1 读到一半,r3 把数据改了怎么办?不安全!
println!("{}, {}, and {}", r1, r2, r3);

这个报错是很多 Rust 新手的噩梦,但它的初衷是保护你的程序不出现诡异的 Bug。

🎉 第二章通关!

目前为止,我们已经搞定了:

  1. 变量与类型: let, mut, i32 vs u8
  2. 内存安全: 所有权 (Move),克隆 (Clone),不可变借用 (&),可变借用 (&mut)。

你现在已经掌握了 Rust 语法的 60% 核心难度!剩下的很多是应用层面的东西。

独酌

独酌

小镇码农

相关文章