https://www.jianshu.com/p/c45d8d96e1ea

介绍

  • 规则:
    1. Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
    2. 值在任一时刻有且只有一个所有者。
    3. 当所有者(变量)离开作用域,这个值将被丢弃。
  • 变量的所有权遵循的模式:
    1. 将值赋给另一个变量时移动它。
    2. 当持有堆中数据的变量离开作用域时,其值将通过 drop 清理掉(除非数据被移动为另一个变量所有)。

移动

:::info 已知大小的数据类型(数字、bool)存储在栈中,其它类型存储在堆中(string)。

:::

  • 堆 Stack
  1. let x = 5;
  2. let y = x;

因为x、y 是数字(固定尺寸),所以数据存放在栈上。

  • 栈 Heap
  1. let s1 = String::from("hello");
  2. let s2 = s1;
  3. println!("{}, world!", s1);
  1. String 由二部分组成:一个指向存放字符串内容内存的指针、长度、容量,这一组数据存储在栈上。另一个是堆上存放内容的部分。
  2. 移动并不是复制堆上的数据,而是复制指针引用到数据。

如果是复制堆数据,当数据较大时会对运行时性能造成非常大的影响。

所有权 - 图1

⚠️ 值得注意

上述提到过当变量离开作用域后,Rust 自动调用 drop 函数清理变量的堆内存。

当两个数据指针指向了同一位置,会有一个问题:当 s2 和 s1 离开作用域,他们都会尝试释放相同的内存。

这是 二次释放(double free)的错误。它会导致内存污染,可能会导致潜在的安全漏洞。

为了确保内存安全,在 let s2 = s1 之后,s1 不再有效(不能再使用),所以第三行代码报错将无法编译。

克隆

  • 堆 Stack
  1. let x = 5;
  2. let y = x;
  3. println!("x = {}, y = {}", x, y);

只在栈上的数据采用拷贝的方式(这样是快速的)。因为互相无关联,所以不存在二次释放的问题。

  • 栈 Heap
  1. let s1 = String::from("hello");
  2. let s2 = s1.clone();
  3. println!("s1 = {}, s2 = {}", s1, s2);

如果要明确的复制堆数据(深拷贝),可以使用 clone 的通用函数。

这里隐含的设计:Rust 永远不会自动创建数据的 “深拷贝”。因此,任何自动的复制可以被认为对运行时性能影响较小。

所有权 - 图2

所有权与函数

  1. fn main() {
  2. let s = String::from("hello"); // s 进入作用域
  3. takes_ownership(s); // s 的值移动到函数里 ...
  4. // ... 所以到这里不再有效
  5. let x = 5; // x 进入作用域
  6. makes_copy(x); // x 应该移动函数里,
  7. // 但 i32 是 Copy 的,所以在后面可继续使用 x
  8. } // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
  9. // 所以不会有特殊操作
  10. fn takes_ownership(some_string: String) { // some_string 进入作用域
  11. println!("{}", some_string);
  12. } // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放
  13. fn makes_copy(some_integer: i32) { // some_integer 进入作用域
  14. println!("{}", some_integer);
  15. } // 这里,some_integer 移出作用域。不会有特殊操作

返回值与作用域

  1. fn main() {
  2. let s1 = gives_ownership(); // gives_ownership 将返回值
  3. // 移给 s1
  4. let s2 = String::from("hello"); // s2 进入作用域
  5. let s3 = takes_and_gives_back(s2); // s2 被移动到
  6. // takes_and_gives_back 中,
  7. // 它也将返回值移给 s3
  8. } // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
  9. // 所以什么也不会发生。s1 移出作用域并被丢弃
  10. fn gives_ownership() -> String { // gives_ownership 将返回值移动给
  11. // 调用它的函数
  12. let some_string = String::from("yours"); // some_string 进入作用域
  13. some_string // 返回 some_string 并移出给调用的函数
  14. }
  15. // takes_and_gives_back 将传入字符串并返回该值
  16. fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
  17. a_string // 返回 a_string 并移出给调用的函数
  18. }