Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain

4.1 所有权的基本概念

所有权(Ownership)是 Rust 语言最核心、最独特的特性。它是一套由编译器在编译时强制执行的内存管理规则,旨在确保内存安全,同时避免了垃圾回收(GC)带来的性能开销。理解所有权是掌握 Rust 的关键。

4.1.1 所有权的核心规则

Rust 的所有权系统遵循三条基本规则:

  1. 每个值在 Rust 中都有一个所有者(Owner)。
  2. 在任意时刻,一个值只能有一个所有者。
  3. 当所有者离开作用域时,这个值将被丢弃(Drop)。

让我们通过代码来理解这些规则。

4.1.2 作用域与所有权

作用域(Scope)是程序中一个项(item)有效的范围。在 Rust 中,作用域通常由花括号 {} 定义。

{                      // s 在这里无效,它尚未声明
    let s = "hello";   // s 从这里开始有效
    // 使用 s
}                      // 此作用域结束,s 不再有效

当变量 s 进入作用域时,它变得有效。当它离开作用域时,它的值就会被丢弃,内存被自动释放。这个过程由编译器自动插入的 drop 函数调用完成,类似于 C++ 中的 RAII(资源获取即初始化)模式。

4.1.3 变量与数据交互的方式

所有权规则在变量与数据交互时表现得尤为明显。主要分为两种情况:移动(Move)和克隆(Clone)。

1. 移动(Move)

对于存储在堆上的复杂数据类型(如 String、Vec),将一个变量赋值给另一个变量时,会发生“移动”。

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

// println!("{}", s1); // 编译错误!s1 不再有效
println!("{}", s2); // 正常,s2 现在是所有者

为什么不是复制?

  • String 由三部分组成:指向堆内存的指针、长度和容量。这部分数据存储在栈上。
  • 字符串的实际内容("hello")存储在堆上。
  • 当 let s2 = s1 时,Rust 复制了栈上的指针、长度和容量。这意味着 s1 和 s2 的指针指向了堆上的同一块内存。
  • 如果同时释放 s1 和 s2,就会导致“双重释放”(double free)错误,这是严重的内存安全问题。
  • 为了避免这个问题,Rust 认为 s1 不再有效。因此,只有 s2 在离开作用域时会释放内存。这被称为“移动”。

2. 克隆(Clone)

如果你确实想要深度复制堆上的数据,而不仅仅是栈上的指针,可以使用 clone 方法。

let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝堆上的数据

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

clone 会复制堆上的数据,因此 s1 和 s2 各自拥有独立的内存。代价是性能开销更大。

3. 复制(Copy)

对于存储在栈上的简单数据类型(如整数 i32、布尔值 bool、浮点数 f64、字符 char、以及由它们组成的元组),赋值操作默认是“复制”。

let x = 5;
let y = x; // x 的值被复制到 y

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

这些类型实现了 Copy trait(特征)。当一个类型实现了 Copy,将其赋值给另一个变量时,旧变量仍然有效。Rust 不允许一个类型同时实现 Copy 和 Drop。如果一个类型或其任何部分实现了 Drop,则不能实现 Copy。

4.1.4 所有权与函数

将值传递给函数与赋值类似。将变量传递给函数时,会发生移动或复制。

fn main() {
    let s = String::from("hello");  // s 进入作用域
    takes_ownership(s);             // s 的值被移动进函数
    // 这里 s 不再有效

    let x = 5;                      // x 进入作用域
    makes_copy(x);                  // x 是 i32 类型,实现了 Copy,所以 x 仍然有效
    println!("x = {}", x);          // 正常
} // x 离开作用域,x 被丢弃。s 已经被移动,无需处理。

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string 离开作用域,drop 被调用,内存被释放

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
} // some_integer 离开作用域,无特殊操作

4.1.5 返回值与作用域

函数也可以将值的所有权返回。

fn main() {
    let s1 = gives_ownership();         // gives_ownership 将返回值移动给 s1

    let s2 = String::from("hello");    // s2 进入作用域

    let s3 = takes_and_gives_back(s2); // s2 被移动进函数,函数将返回值移动给 s3
} // s3 离开作用域,被丢弃。s1 离开作用域,被丢弃。s2 已被移动,无需处理。

fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string // 返回 some_string,将所有权移出函数
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string // 返回 a_string,将所有权移出函数
}

这种通过函数参数和返回值来转移所有权的方式虽然可行,但会显得繁琐。幸运的是,Rust 提供了**引用(References)和借用(Borrowing)**机制,允许你在不转移所有权的情况下使用值。这将是下一节的核心内容。

4.1.6 总结

  • 所有权是 Rust 管理内存的核心机制,无需垃圾回收器。
  • 每个值有且只有一个所有者。
  • 当所有者离开作用域,值被自动丢弃。
  • 对于堆上的复杂类型,赋值默认是移动,旧变量失效。
  • 使用 clone 可以进行深拷贝。
  • 栈上的简单类型(实现了 Copy trait)赋值默认是复制。
  • 函数参数和返回值也会导致所有权的转移。

掌握所有权的移动规则,是理解 Rust 内存安全和编写高效、无数据竞争代码的第一步。

Last Updated:: 5/9/26, 3:13 PM