所有权系统的计算机科学基础
原理
Rust 所有权根植于线性逻辑中 affine 类型理论。在线性逻辑中,命题是”资源”,恰好使用一次。Rust 将约束放宽为”最多一次”:每个非 Copy 值恰好被消耗(move)或销毁(drop)一次。
形式化推导规则:
Move 语义在机器码层面仅为 memcpy(栈上 24B 的 ptr+len+cap),原变量位置被编译器标记为”已移动”,后续使用触发静态错误。Drop 插入位置由 MIR 的 drop elaboration pass 确定——在所有者离开作用域的每条控制流路径上自动插入 drop(x) 调用。
对比:
| 语言 | 释放方式 | 运行时开销 | 双释放风险 |
|---|---|---|---|
| C | 手动 free() | 零 | 高 |
| C++ | RAII / unique_ptr | 零 | 中 |
| Java/Go | GC trace | 高 | 零 |
| Rust | 编译期 drop 插入 | 零 | 零 |
编译器确定 drop 位置的算法基于支配树:找到所有者不再存活的程序点(后支配边界),插入释放。
语法
所有权转移的核心规则
let s1 = String::from("hello");
let s2 = s1; // move: s1 失效
// println!("{}", s1); // 编译错误
let x = 5;
let y = x; // Copy: x 仍有效Drop 与作用域
{
let s = String::from("hi"); // s 获得所有权
println!("{}", s); // s 使用中
} // s.drop() 在此插入
fn consume(s: String) { } // 参数接收所有权,结束时 drop所有权图
非词法生命周期(NLL)
let mut s = String::from("hi");
let r1 = &s;
println!("{}", r1); // r1 最后使用
let r2 = &mut s; // OK: r1 在此前已"死亡"(NLL)实践
力扣问题
力扣: 力扣大数运算题 — 大数所有权传递
fn multiply(a: Vec<u32>, b: Vec<u32>) -> Vec<u32> {
// a 和 b 的所有权移入,返回新 Vec
let mut result = vec![0; a.len() + b.len()];
result
}AI 自检
- 线性类型与 affine 类型的区别?Rust 为何选择 affine(最多一次)而非线性(恰好一次)?
- 编译器在什么编译阶段(HIR/MIR/LLVM IR)决定 drop 插入的位置?支配树的作用?