Rust: 理解所有权

所有权是 Rust 最独特的功能,对语言的其余部分有着深远的影响。它使 Rust 能够在不需要垃圾收集器的情况下保证内存安全,因此了解所有权的工作原理非常重要。

0x01. 什么是所有权?

所有权是一组规则,用于控制 Rust 程序如何管理内存。

0x02. 所有权规则

  • 每个值都有一个所有者。
  • 一次只能有一个所有者。
  • 当所有者超出范围时,该值将被删除。

2.1 每个值都有一个所有者

Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者。

2.2 所有者超出范围时,该值将被删除

1
2
3
4
5
{                      // s is not valid here, it’s not yet declared
let s = "hello"; // s is valid from this point forward

// do stuff with s
} // this scope is now over, and s is no longer valid

此外,这还隐含着一种设计选择:Rust 永远不会自动创建数据的“深层”副本。因此,可以认为任何自动复制在运行时性能方面都是低成本的。

2.3 一次只能有一个所有者。(变量与数据的交互)

String 由三部分组成:指向保存字符串内容的内存的指针、长度和容量。这组数据存储在栈上。字符串内容保存在堆上。

1
2
3
4
5
6
7
# 仅栈数据:复制
let x = 5; # 5
let y = x; # 5

# 所有权转移
let s1 = String::from("hello");
let s2 = s1;

0x03. 参数传递

将值传递给函数的机制与将值赋给变量的机制类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn main() {
let s = String::from("hello"); // s comes into scope

takes_ownership(s); // s's value moves into the function...
// ... and so is no longer valid here

let x = 5; // x comes into scope

makes_copy(x); // x would move into the function,
// but i32 is Copy, so it's okay to still
// use x afterward

} // Here, x goes out of scope, then s. But because s's value was moved, nothing
// special happens.

fn takes_ownership(some_string: String) { // some_string comes into scope
println!("{some_string}");
} // Here, some_string goes out of scope and `drop` is called. The backing
// memory is freed.

fn makes_copy(some_integer: i32) { // some_integer comes into scope
println!("{some_integer}");
} // Here, some_integer goes out of scope. Nothing special happens.

0x04. 函数返回

返回值也可以转移所有权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
fn main() {
let s1 = gives_ownership(); // gives_ownership moves its return
// value into s1

let s2 = String::from("hello"); // s2 comes into scope

let s3 = takes_and_gives_back(s2); // s2 is moved into
// takes_and_gives_back, which also
// moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 was moved, so nothing
// happens. s1 goes out of scope and is dropped.

fn gives_ownership() -> String { // gives_ownership will move its
// return value into the function
// that calls it

let some_string = String::from("yours"); // some_string comes into scope

some_string // some_string is returned and
// moves out to the calling
// function
}

// This function takes a String and returns one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
// scope

a_string // a_string is returned and moves out to the calling function
}