Rust 所有权机制:为什么它如此重要

· 更新于 2026-04-06 · 2072 字
目录

Rust 的所有权系统是它最独特也最难理解的特性。本文用直觉和类比帮你建立正确的心智模型。

核心规则

Rust 的所有权只有三条规则:

  1. 每个值都有一个所有者(owner)
  2. 同一时刻只能有一个所有者
  3. 当所有者离开作用域,值被丢弃(drop)
1
2
3
4
5
6
7
fn main() {
let s1 = String::from("hello"); // s1 是所有者
let s2 = s1; // 所有权转移给 s2,s1 不再有效

// println!("{}", s1); // ❌ 编译错误!s1 已经无效
println!("{}", s2); // ✅ 正常
}

移动语义 (Move)

对于堆上数据(如 String),赋值意味着移动而非复制:

1
2
3
4
5
6
let name = String::from("Alice");
let greeting = format!("Hello, {}!", name);
// name 仍然有效,因为 format! 借用了 name

let other_name = name;
// name 已被移动,不再有效

这和 C++ 的移动语义类似,但 Rust 在编译期强制执行,不存在”移动后使用”的风险。

借用 (Borrowing)

不想转移所有权?用引用借用:

1
2
3
4
5
6
7
8
9
fn calculate_length(s: &String) -> usize {
s.len()
}

fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // 借用 s
println!("'{}' 的长度是 {}", s, len); // s 仍然有效
}

可变引用

1
2
3
4
5
6
7
8
9
fn append_world(s: &mut String) {
s.push_str(", world!");
}

fn main() {
let mut s = String::from("hello");
append_world(&mut s);
println!("{}", s); // "hello, world!"
}

关键限制: 同一作用域内,要么有一个可变引用,要么有多个不可变引用,不能同时存在。

1
2
3
4
5
let mut s = String::from("hello");
let r1 = &s; // ✅
let r2 = &s; // ✅ 多个不可变引用
// let r3 = &mut s; // ❌ 不能同时有可变引用
println!("{}, {}", r1, r2);

生命周期 (Lifetimes)

编译器需要确保引用不会比被引用的数据活得更久:

1
2
3
4
5
6
7
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}

'a 标注告诉编译器:返回的引用至少和 xy 中较短的那个活得一样久。

与 GC 的对比

特性 Rust (所有权) Java/Go (GC) C (手动)
内存安全 编译期保证 运行时保证 不保证
性能开销 GC 暂停
并发安全 编译期保证 部分保证 不保证
学习曲线 陡峭 平缓 中等

实践建议

  1. 先用 .clone() 让代码跑起来,再优化所有权
  2. 函数参数优先用引用 &T,除非确实需要获取所有权
  3. 小类型(i32, bool 等)实现了 Copy,不需要担心移动
  4. 遇到生命周期问题,先尝试缩小引用的作用域

“与借用检查器搏斗”是每个 Rust 初学者的必经之路。但一旦你理解了它,你会发现它其实在帮你写出更好的代码。

评论