logo

第 3 章:所有权和借用

作者
Modified on
Reading time
3 分钟阅读:..评论:..

Rust 语言的核心特性之一就是所有权系统。这一系统使得 Rust 程序在编译时就能捕获许多内存错误,确保内存安全和高效。理解所有权、借用和生命周期是掌握 Rust 编程的关键。本章将详细解释这些概念,并通过示例代码和图表帮助你深入理解它们。

什么是所有权

在 Rust 中,每一个值都拥有一个所有者(owner),这个所有者在任何时候都只能有一个。当所有者不再使用这个值时,该值的内存将被释放。这种独特的所有权模型避免了许多常见的内存管理错误,如空悬指针和双重释放。

所有权规则

  1. 每一个值都有一个所有者。
  2. 每个值在任一时刻只能有一个所有者。
  3. 当所有者离开作用域,值会被丢弃。

代码示例

让我们通过一个简单的代码示例来理解所有权:

fn main() { let s1 = String::from("hello"); let s2 = s1; // s1 的所有权转移给了 s2 // 现在 s1 不再有效 println!("{}", s1); // 这行代码会导致编译错误 }

在上面的代码中,s1 的所有权被转移给了 s2。此后,s1 不再有效,尝试使用 s1 将会导致编译错误。

graph LR A[变量a] -- 所有权转移 --> B[变量b] B -- 所有权转移 --> C[变量c] C -- 所有权转移 --> D[变量d]

借用和引用

在 Rust 中,借用(borrowing)允许你在不转移所有权的情况下使用值。借用通过引用(references)来实现,引用分为可变引用和不可变引用。

不可变引用

不可变引用允许你读取值,但不能修改值。

fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len); } fn calculate_length(s: &String) -> usize { s.len() }

在上述代码中,calculate_length 函数借用了 s1 的不可变引用。这种借用是安全的,因为在借用期间,值是不可变的。

可变引用

可变引用允许你修改值,但在任一时间点,只能存在一个可变引用,或者多个不可变引用。

fn main() { let mut s = String::from("hello"); change(&mut s); println!("{}", s); } fn change(some_string: &mut String) { some_string.push_str(", world"); }

在上述代码中,change 函数借用了 s 的可变引用,并且修改了它。

生命周期

生命周期(lifetimes)是 Rust 用来跟踪引用有效性的机制。生命周期注解告诉编译器各种引用之间的关系,有助于避免悬垂引用(dangling references)。

生命周期注解

生命周期注解使用 'a 这样的语法。虽然生命周期注解看起来复杂,但它们通常是编译器在编译时推断的。

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }

在上述代码中,longest 函数的生命周期注解 'a 指示返回值的生命周期与输入参数的生命周期相关。

图解生命周期

graph LR A[引用a] -- 生命周期'a --> B[引用b] B -- 生命周期'b --> C[引用c] C -- 生命周期'c --> D[引用d]

小结

本章介绍了 Rust 中所有权、借用和生命周期的概念,这些特性是 Rust 内存安全的基础。通过理解和正确使用这些概念,你可以编写出高效且安全的代码。在接下来的章节中,我们将继续深入探索 Rust 的其他高级特性和应用。 【本章节完毕】