Rust 所有权与借用规则
更新时间:2026-09-18。这是 Rust 内存安全的地基。本篇是原子点速通,更深入的图解见 所有权与借用深入。
一、三条所有权规则
- Rust 中每个值都有且仅有一个**所有者(owner)**变量。
- 同一时刻值只有一个所有者;所有者离开作用域,值被自动
drop释放。 - 值可以被借用(引用),但借用必须遵守借用规则。
rust
let s1 = String::from("hi"); // s1 拥有堆上数据
let s2 = s1; // 所有权「移动」给 s2,s1 失效
// println!("{}", s1); // 编译错误:value borrowed after move二、移动 vs Copy
- 拥有堆资源的类型(
String、Vec等)赋值/传参是移动(只拷栈上的指针/长度/容量,不深拷贝,原变量失效)。 - 标量与固定大小类型(
i32、bool、(i32,i32))实现了Copy,赋值是按位复制,原变量仍可用。
三、借用规则(核心)
在任意时刻,要么有任意多个不可变引用
&T,要么有唯一一个可变引用&mut T;二者不能同时存在。引用必须始终有效(不能悬垂)。
rust
let mut v = vec![1, 2, 3];
let r1 = &v;
let r2 = &v; // 多个不可变引用 OK
println!("{} {}", r1[0], r2[0]);
let w = &mut v; // r1/r2 此后不再使用(NLL),才允许可变借用
w.push(4);这条规则在编译期就消灭了数据竞争(「并发写时有人读」无法通过编译),无需 GC、无需运行时锁。
四、高频编译错误与修法
| 报错 | 原因 | 修法 |
|---|---|---|
use of moved value | 值被 move 后继续用 | 借用 &s;需要副本用 s.clone();实现 Copy(仅小标量) |
cannot borrow as mutable because it is also borrowed as immutable | 读引用与写引用共存 | 缩小读引用作用域,让其最后一次使用早于可变借用(NLL 已放宽) |
missing lifetime specifier | 返回引用来源不明 | 标注生命周期,见 生命周期标注与省略 |
| 函数里修改不了外部变量 | 传的是不可变引用/所有权 | 传 &mut x,调用处 &mut,变量声明 mut |
函数传参:读用 &T、写用 &mut T、要拿走所有权用 T。返回引用不会夺取所有权,但要写清生命周期。
五、常见坑
- 以为
let y = x是拷贝:对 String/Vec 这是移动,旧变量失效,这是和 Java/JS 最大的直觉差异。 - 在持有
&v时v.push:不可变借用期间不能可变借用,调整作用域或拷贝。 - 循环中同时遍历和修改:
for x in &v { v.push(...) }编译失败,用索引或先收集改动。 - 把借用发进线程:跨线程引用需要
'static或用Arc,见 Rust async/Future 与 Send/Sync。
总结
Rust 用所有权让每个值只有一个主人、离开作用域即自动释放,用移动(String/Vec 只转移所有权不深拷贝)和 Copy(标量按位复制)区分赋值语义,用「同时多个 &T 或唯一 &mut T、引用必须有效」的借用规则在编译期同时杜绝悬垂指针和数据竞争;常见 move 后再用、读写借用共存、跨线程发引用等报错,分别靠借用、clone、缩小作用域(NLL)和标注生命周期/Arc 解决。