本页目录

所有权模型

Rust 的所有权模型只有三条规则:每个值有唯一所有者、所有者离开作用域时值被 drop、赋值和传参会转移所有权(move)。Copy 是 move 的例外(位复制后两个变量都可用),Clone 是显式深拷贝,partial move 让你从结构体中搬走部分字段。这三条规则是 Rust 不用 GC 却能保证内存安全的根基。

为什么 Rust 需要所有权

C 的手动 malloc/free 和 C++ 的 RAII 都依赖程序员纪律——忘记释放、double free、use-after-free 在编译期完全不可见。GC 语言自动管理但引入运行时开销和 stop-the-world 延迟。Rust 的答案:⁠在编译期通过所有权规则保证内存安全,零运行时开销⁠。

核心三条规则:

  1. Rust 中每个值有且仅有一个 owner(所有者)
  2. owner 离开作用域时,值被 drop
  3. 赋值/传参/返回值会转移(move)所有权,除非类型实现了 Copy

Move Semantics

let s1 = String::from("hello");
let s2 = s1;                         // s1 的所有权移动到 s2
// println!("{}", s1);              // ERROR: s1 已被 move, 不能再使用

String 在栈上存储 3 个 word(指针、长度、容量),实际数据在堆上。let s2 = s1 是浅拷贝(复制栈上的 3 个 word),然后标记 s1 失效——⁠不会发生 double free,因为只有 s2 会在作用域结束时调用 drop 释放堆内存。

编译器实际生成的伪代码:

s1 = String { ptr → heap[5 bytes "hello"], len: 5, cap: 5 }
s2 = String { ptr → same heap, len: 5, cap: 5 }   // move: 复制栈上字段
s1 被标记为 "moved out" → drop(s1) 被编译器跳过

Copy 与 Clone

let x = 42;
let y = x;                           // x 仍然可用 — i32 实现了 Copy
println!("{} {}", x, y);             // OK

Copy trait 是编译器自动识别的标记⁠——实现了 Copy 的类型在赋值时是 bitwise copy 而非 move。条件是:类型中的所有字段都必须实现了 Copy。i32boolchar&T 实现了 Copy;StringVec<T>Box<T> 没有——因为它们有堆数据。

Clone显式的深拷贝⁠:.clone() 由程序员调用,可以分配新的堆内存。String 实现了 Clone 但没有 Copy——显式 .clone() 产生新的 String,不会发生所有权转移。

let s1 = String::from("hello");
let s2 = s1.clone();                 // 深拷贝: 新的 heap allocation
println!("{} {}", s1, s2);           // 两个独立的 String, 都有效

Move 发生的位置

// 1. 赋值
let s2 = s1;                         // move

// 2. 传参
fn take(s: String) {}                // s 获得所有权
take(s1);                            // s1 move into s → s1 失效

// 3. 返回值
fn give() -> String { String::from("hi") }  // 所有权随返回值转移给 caller

// 4. 模式匹配
let (a, b) = (vec![1], vec![2]);     // 两个 Vec 分别 move 到 a 和 b

// 5. 闭包捕获
let v = vec![1, 2, 3];
let closure = || { println!("{:?}", v); };  // 闭包 captured v by move (因为 Vec 不实现 Copy)
// v 不能再被外部使用!

Stack 数据 vs Heap 数据

理解 move 和 copy 的关键:⁠栈上的数据 bitwise copy 是安全的——所有权转移只需要复制栈上内容⁠。堆数据的所有权转移也只需要复制栈上的指针,堆上的数据不复制。

let s1 = String::from("hello");      // stack: [ptr|len|cap], heap: "hello"
let s2 = s1;                         // stack copy: [same ptr|same len|same cap]
                                     // s1 标记 moved → drop(s1) 不调用
// 作用域结束: drop(s2) → 释放 heap 上的 "hello"

Copy 的条件

什么类型自动实现了 Copy?

  • 所有字段都实现了 Copy
  • 没有实现 Drop(如果有 Drop,说明有资源需要释放——不能是简单的 bitwise copy)
#[derive(Copy, Clone)]               // 手动 derive Copy (需 also Clone)
struct Point { x: i32, y: i32 }      // 所有字段都 Copy → Point 也是 Copy

struct MyString(String);             // String 不 Copy → MyString 不 Copy
// 不能 derive Copy for MyString!

部分移动 (Partial Move)

let p = (String::from("hello"), 42);
let (s, n) = p;                      // s 获得了 String 的所有权, n copy 了 42
// println!("{:?}", p);             // ERROR: p.0 已被 move
println!("{}", p.1);                 // OK: p.1 是 i32, Copy 了, 仍在 p 中

Drop 顺序

作用域结束时,变量按声明的逆序被 drop:

{
    let a = String::from("first");   // 先声明
    let b = String::from("second");  // 后声明
}                                    // drop(b) → drop(a)

这对安全性很重要——后面的变量可能引用前面的变量,先 drop 后面的保证了引用始终有效。

参考

  • Rust Book: Chapter 4.1 — What is Ownership
  • Rust Reference: destructors, move semantics
  • RFC: RFC 213 (default Move semantics)

Keywords: ownership, move, Copy, Clone, stack, heap, drop, partial move, RAII