章节目录

所有权、移动、复制与析构

本章目标

这一章进入 Rust 最核心的规则:每个值都有所有者,所有者离开作用域时值被释放,赋值和传参可能发生移动。你会理解 Copy 为什么特殊,以及为什么这些规则能替代垃圾回收。

它是什么

所有权是 Rust 管理内存的制度。每个值在同一时刻只有一个所有者。所有者离开作用域,值就会被清理。

{
    let text = String::from("hello");
}

当程序离开花括号,text 被释放,它持有的堆内存也被释放。

为什么需要

没有所有权,程序需要用垃圾回收或手动释放管理内存。垃圾回收简单但有运行时成本;手动释放灵活但容易出现悬垂指针、重复释放、泄漏。

Rust 的选择是:编译期追踪谁拥有资源,谁负责释放。这样大部分错误在编译时被挡住。

怎么使用

移动示例:

let a = String::from("hello");
let b = a;

println!("{b}");

a 的所有权移动给 b,之后不能再使用 a。如果你想保留两份独立字符串,需要显式克隆:

let a = String::from("hello");
let b = a.clone();
println!("{a} {b}");

逐行解释

fn take(text: String) {
    println!("{text}");
}

let name = String::from("RinBlog");
take(name);
  • fn take(text: String) 表示函数接收一个拥有所有权的 String
  • 调用 take(name) 时,name 的所有权移动进函数参数 text
  • 函数体中可以使用 text
  • 函数结束时,text 离开作用域,字符串被释放。
  • 调用后不能再使用 name,因为它已经不再拥有值。

如果函数只是读取字符串,应改成接收 &str&String,也就是借用。

常见坑

  • StringVec<T>HashMap<K, V> 默认移动,不自动复制。
  • 整数、布尔值、字符等简单类型通常实现 Copy,赋值后还能继续用原变量。
  • clone() 是显式复制,能解决所有权问题,但可能有性能成本。
  • 移动不是运行时搬运大块内存,很多时候只是把栈上的指针、长度、容量转移给新变量。

练习

  1. 写一个函数接收 String,调用后尝试继续使用原变量,观察报错。
  2. 把函数参数改成 &str,再观察是否能继续使用原变量。
  3. String 使用 clone(),比较行为。
  4. i32 做同样实验,理解 Copy

造轮子任务

写一个资源盒子 struct Resource { name: String },实现一个函数 consume(resource: Resource)。打印构造、移动、消费的过程。目标是用日志观察所有权从一个名字转到另一个名字。

小结

所有权回答“谁负责释放”。移动让责任转移,Copy 让简单值复制,clone 让复杂值显式复制。理解这一章后,后面的借用规则才有意义。