https://www.jianshu.com/p/c45d8d96e1ea
介绍
- 规则:
- Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
- 值在任一时刻有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
- 变量的所有权遵循的模式:
- 将值赋给另一个变量时移动它。
- 当持有堆中数据的变量离开作用域时,其值将通过 drop 清理掉(除非数据被移动为另一个变量所有)。
移动
:::info 已知大小的数据类型(数字、bool)存储在栈中,其它类型存储在堆中(string)。
:::
- 堆 Stack
let x = 5;
let y = x;
因为x、y 是数字(固定尺寸),所以数据存放在栈上。
- 栈 Heap
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
- String 由二部分组成:一个指向存放字符串内容内存的指针、长度、容量,这一组数据存储在栈上。另一个是堆上存放内容的部分。
- 移动并不是复制堆上的数据,而是复制指针引用到数据。
如果是复制堆数据,当数据较大时会对运行时性能造成非常大的影响。
⚠️ 值得注意
上述提到过当变量离开作用域后,Rust 自动调用 drop 函数清理变量的堆内存。
当两个数据指针指向了同一位置,会有一个问题:当 s2 和 s1 离开作用域,他们都会尝试释放相同的内存。
这是 二次释放(double free)的错误。它会导致内存污染,可能会导致潜在的安全漏洞。
为了确保内存安全,在 let s2 = s1 之后,s1 不再有效(不能再使用),所以第三行代码报错将无法编译。
克隆
- 堆 Stack
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
只在栈上的数据采用拷贝的方式(这样是快速的)。因为互相无关联,所以不存在二次释放的问题。
- 栈 Heap
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
如果要明确的复制堆数据(深拷贝),可以使用 clone 的通用函数。
这里隐含的设计:Rust 永远不会自动创建数据的 “深拷贝”。因此,任何自动的复制可以被认为对运行时性能影响较小。
所有权与函数
fn main() {
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s 的值移动到函数里 ...
// ... 所以到这里不再有效
let x = 5; // x 进入作用域
makes_copy(x); // x 应该移动函数里,
// 但 i32 是 Copy 的,所以在后面可继续使用 x
} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
// 所以不会有特殊操作
fn takes_ownership(some_string: String) { // some_string 进入作用域
println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放
fn makes_copy(some_integer: i32) { // some_integer 进入作用域
println!("{}", some_integer);
} // 这里,some_integer 移出作用域。不会有特殊操作
返回值与作用域
fn main() {
let s1 = gives_ownership(); // gives_ownership 将返回值
// 移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到
// takes_and_gives_back 中,
// 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
// 所以什么也不会发生。s1 移出作用域并被丢弃
fn gives_ownership() -> String { // gives_ownership 将返回值移动给
// 调用它的函数
let some_string = String::from("yours"); // some_string 进入作用域
some_string // 返回 some_string 并移出给调用的函数
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}