什么是所有权?
所有权是 Rust 的核心特性之一,它决定了如何管理内存以及何时释放内存。Rust 使用所有权系统来保证程序的安全性和高效性,而无需像 C 或 C++ 那样依赖于手动管理内存。所有权系统由以下几部分组成:
- 所有权
- 借用
- 生命周期
所有权规则
Rust 有三个核心的所有权规则,理解这些规则对于编写有效的 Rust 代码至关重要:
- 在一个作用域内的每一个值都有一个所有者。
- 值在同一时间只能有一个所有者。
- 当所有者离开作用域,这个值将被丢弃。
所有权与变量绑定
当一个值被赋予一个变量时,这个变量就成为了该值的所有者。例如:
fn main() { let s = "hello"; // `s` 现在是字符串字面量的所有者 }
在这个例子中,s
是字符串字面量 "hello"
的所有者。一旦 s
离开作用域,这个字符串字面量就会被丢弃。然而,字符串字面量实际上是在编译时被静态分配的内存,因此不会真正地被丢弃。
所有权转移
当一个值赋给另一个变量时,原来的变量不再拥有这个值。这被称为所有权转移。例如:
fn main() { let s1 = String::from("hello"); let s2 = s1; // 所有权从 `s1` 转移到了 `s2` println!("{}", s2); // 正确 // println!("{}", s1); // 错误:使用已转移所有权的变量 }
在上面的例子中,s1
将其拥有的 String
值转移给了 s2
。之后尝试访问 s1
将导致编译错误,因为 s1
已经失去了对 String
的所有权。
所有权与函数
当一个值作为参数传递给函数时,该值的所有权也会转移给函数。例如:
-- -------------------- ---- ------- -- ------ - --- - - ---------------------- ------------------- -- --- ---------- --- - - -- -------------- -- --- --------- - -- ---------------------------- ------- - -- ----------- ----- -------------- ------------- - -- ----------- -------- ------------- --- -- ------------------------ ---- - -- ------------ ----- -------------- -------------- - -- ------------ --------------- ---- ----- -----------展开代码
在这个例子中,s
的所有权被传递给 takes_ownership
函数,因此在函数内部可以安全地使用 s
。而在 makes_copy
函数中,由于 i32
类型实现了 Copy
trait,它的值被复制了一份传递给函数,因此原始值 x
仍然有效。
返回值与所有权
函数也可以返回一个值的所有权。例如:
-- -------------------- ---- ------- -- ------ - --- -- - ------------------ -- ----------------- ------------ ---- --- -- - ---------------------- --- -- - ------------------------- -- ---- -------- ---------------------- --------- ---- - -- ----------------- -- ------ - -- ----------------- --------------- --- ----------- - ---------------------- ----------- - -- ------------------------------ ------- -- ------ - -- ---------- -------- ---------------------- -- -------- -展开代码
在这个例子中,gives_ownership
函数创建了一个新的 String
并将其所有权转移给调用者。takes_and_gives_back
函数接受一个 String
参数,并将其所有权再次转移给调用者。
引用与借用
引用允许我们访问一个值而不获取其所有权。这有助于避免所有权转移带来的复杂性,并且可以在不转移所有权的情况下共享数据。引用分为不可变引用和可变引用两种类型。
不可变引用
不可变引用通过 &
符号来创建。例如:
-- -------------------- ---- ------- -- ------ - --- - - ---------------------- --- --- - --------------------- -- --------- ------------- ------ -- ---- -- ----- -- ----- - -- ------------------- -------- -- ----- - -- --- ----- -------- ------ ------- -展开代码
在这个例子中,calculate_length
函数接受一个不可变引用作为参数。这允许我们在不转移所有权的情况下计算 String
的长度。
可变引用
可变引用通过 &mut
符号来创建。一个值在任何时刻只能有一个可变引用。例如:
-- -------------------- ---- ------- -- ------ - --- --- - - ---------------------- ----------- --- -- -------- -------------- --- - -- ------------------- ---- ------- - -- ------------- ----- -------- ----- ----------------------- -------- -展开代码
在这个例子中,change
函数接受一个可变引用作为参数。这允许我们在不转移所有权的情况下修改 String
的内容。
生命周期
生命周期确保引用总是有效的。Rust 编译器会自动推断大多数引用的生命周期,但在某些情况下需要显式指定。生命周期注解通常放在引用类型的前面。例如:
-- -------------------- ---- ------- -- -------------- --- ---- -- --- ---- -- --- --- - -- ---- --------- -- ------- - ------- - - - ---- - - - - -- ------ - --- ------- - ------------------ ------ -- ------- - --- ------- - -------------------- --- ------ - ------------------------- ------------------ ------------- ------- ------ -- ---- -------- - -展开代码
在这个例子中,longest
函数接受两个引用,并返回一个引用。生命周期注解 'a
确保返回的引用始终有效。
总结
本章介绍了 Rust 的所有权系统,包括所有权、借用和生命周期的概念。理解这些概念对于编写高效的 Rust 代码至关重要。通过所有权转移、引用和生命周期注解,Rust 提供了一种安全且高效的方式来管理内存。