第四节、所有权
所有权机制
Rust 为了做内存管理引入所有权机制,分配内存的时候创建一个所有权,所有权作用域消失,就要释放内存。这是Rust创立的新概念。
传统编程语言的内存管理: * 垃圾回收机制(GC),例如:Python,Java ,Go * 手动管理内存的分配和释放,例如:C语言
如果你有编程语言基础,尤其是C语言的基础,那么这个所有权机制非常简单。
堆和栈
- 堆,是在程序运行过程中调用操作系统来管理内存的机制,例如:Linux的malloc和free。
- 栈,是编译器根据操作系统原理在特殊区域来分配内存的机制,一般的CPU都有相关的栈寄存器。
通常堆是大内存的时候使用,栈是小内存的时候使用。
所有权规则
首先,让我们看一下所有权规则。
- Rust 中的每个值都有一个名为owner的变量。
- 一次只能有一个所有者。
- 当所有者离开作用域时,该内存将被删除(释放)。
变量的作用域
let s = "hello"
为了更清晰的说明问题,我们要介绍一下String类型
上面定义的字符串,在使用的过程中不可以追加和修改。在实际操作的过程中,我们会利用字符串函数来生成可变的字符串。字符串除了内容,还有长度的标识。
字符串申请
let s = String::from("hello")
s.push_str(", world!"); //追加内容
如果此时我们执行下面的代码所有权将会发生怎样的变化呢?
let s1 = String::from("hello");
let s2 = s1;
此时hello的所有权已经是s2,s1不能再使用。
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
所以要解决s1,s2都能用问题。 String有一个clone函数
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
栈复制
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
答案是: NO
为什么呢?
这里其实发生了栈复制,编译器会在栈空间 复制了一个5,也就是2个5。 每个都5都有自己的所有权,也就是x,y都分别有自己的5。
那么哪些类型会形成栈复制?
* 所有整数类型,比如 u32。
* 布尔类型,bool,它的值是 true 和 false。
* 所有浮点数类型,比如 f64。
* 字符类型,char。
* 元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是。
函数传值与所有权
fn main() {
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // 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("hello"); // 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 并移出给调用的函数
}