6·所有权系统入门

所有权(Ownership)

所有权(Ownership)

学习目标

  1. 理解 Rust 的所有权规则
  2. 理解 Move 语义
  3. 理解栈和堆的区别
  4. 掌握 Clone 和 Copy 的区别

核心概念

所有权三条规则

1. Rust 中每个值都有一个所有者(owner)
2. 同一时刻只能有一个所有者
3. 所有者离开作用域时,值被自动丢弃(drop)

栈和堆

栈(Stack)          堆(Heap)
┌──────────┐        ┌──────────┐
│ 快速分配  │        │ 慢速分配  │
│ 固定大小  │        │ 动态大小  │
│ 自动释放  │        │ 手动管理  │
│ i32, bool│        │ String   │
│ 原始类型  │        │ Vec      │
└──────────┘        └──────────┘

Move 语义

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1 的所有权移动到 s2

    // println!("{}", s1);  // ❌ 编译错误:s1 已失效
    println!("{}", s2);     // ✅ s2 是新的所有者
}

为什么需要 Move?

s1 (栈)          堆内存
┌──────────┐    ┌──────────┐
│ ptr ────────→ │ "hello"  │
│ len: 5   │    └──────────┘
│ cap: 5   │
└──────────┘

s2 = s1 之后:

s1 (已失效)      s2 (栈)          堆内存
┌──────────┐    ┌──────────┐    ┌──────────┐
│ ✗ 无效   │    │ ptr ────────→ │ "hello"  │
└──────────┘    │ len: 5   │    └──────────┘
                │ cap: 5   │
                └──────────┘

如果 s1 和 s2 都指向同一块堆内存,两个都离开作用域时会 double free。Move 避免了这个问题。

Clone(深拷贝)

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();  // 深拷贝:堆上的数据也复制了

    println!("s1 = {}, s2 = {}", s1, s2);  // ✅ 都有效
}

Copy trait(栈上类型自动复制)

fn main() {
    let x = 5;
    let y = x;  // i32 实现了 Copy,自动复制

    println!("x = {}, y = {}", x, y);  // ✅ 都有效
}

实现了 Copy 的类型:

  • 所有整数类型(i32, u64 等)
  • 布尔类型 bool
  • 浮点类型 f32, f64
  • 字符类型 char
  • 元组(如果所有元素都是 Copy):(i32, f64) 是 Copy

没有实现 Copy 的类型

  • String
  • Vec<T>
  • 任何需要堆分配的类型

函数与所有权

fn take_ownership(s: String) {
    println!("{}", s);
}   // s 离开作用域,被 drop

fn make_copy(x: i32) {
    println!("{}", x);
}   // x 离开作用域,但 i32 是 Copy,不影响调用者

fn main() {
    let s = String::from("hello");
    take_ownership(s);     // s 的所有权移动到函数内
    // println!("{}", s);  // ❌ s 已失效

    let x = 5;
    make_copy(x);          // x 复制了一份传进去
    println!("{}", x);     // ✅ x 仍然有效
}

返回值与所有权

fn give_string() -> String {
    let s = String::from("hello");
    s  // 所有权转移给调用者
}

fn take_and_give_back(s: String) -> String {
    println!("{}", s);
    s  // 还给调用者
}

fn main() {
    let s1 = give_string();
    let s2 = take_and_give_back(s1);
    // println!("{}", s1);  // ❌ 已移动
    println!("{}", s2);     // ✅
}

实践练习

练习 1:理解 Move

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;
    // println!("{}", s1);  // 这行会报什么错?试试看

    let s3 = s2.clone();
    println!("s2 = {}, s3 = {}", s2, s3);  // ✅
}

练习 2:函数所有权转移

fn print_string(s: String) {
    println!("{}", s);
}

fn main() {
    let s = String::from("hello");
    print_string(s);
    // 如何让 s 继续可用?提示:clone 或返回
}

练习 3:判断 Copy 还是 Move

fn main() {
    // 以下哪些会报错?为什么?

    // 1)
    let a = 42;
    let b = a;
    println!("{}, {}", a, b);

    // 2)
    let s1 = String::from("hi");
    let s2 = s1;
    // println!("{}, {}", s1, s2);

    // 3)
    let t1 = (1, 2.0, true);
    let t2 = t1;
    println!("{:?}, {:?}", t1, t2);

    // 4)
    let v1 = vec![1, 2, 3];
    let v2 = v1;
    // println!("{:?}, {:?}", v1, v2);
}

常见错误

1. 使用已移动的值

let s = String::from("hello");
let s2 = s;
println!("{}", s);  // ❌ value used here after move

2. 误以为赋值是复制

let v = vec![1, 2, 3];
let v2 = v;  // 这是 Move,不是复制!
// v 已失效

3. 在循环中移动

let s = String::from("hello");
for _ in 0..3 {
    // println!("{}", s);  // ❌ 第二次循环时 s 已被移动
}
// ✅ 应该用 &s(借用)或 s.clone()

小结

概念说明
所有权每个值只有一个所有者
Move赋值/传参时所有权转移,原变量失效
Clone显式深拷贝
Copy栈上类型自动复制,原变量仍有效
Drop离开作用域时自动释放

一句话:默认是 Move,想复制要显式调用 .clone()

练习编辑器

rust
Loading...