Skip to content

第四节、所有权

所有权机制

Rust 为了做内存管理引入所有权机制,分配内存的时候创建一个所有权,所有权作用域消失,就要释放内存。这是Rust创立的新概念。

传统编程语言的内存管理: * 垃圾回收机制(GC),例如:Python,Java ,Go * 手动管理内存的分配和释放,例如:C语言

如果你有编程语言基础,尤其是C语言的基础,那么这个所有权机制非常简单。

堆和栈

  • 堆,是在程序运行过程中调用操作系统来管理内存的机制,例如:Linux的malloc和free。
  • 栈,是编译器根据操作系统原理在特殊区域来分配内存的机制,一般的CPU都有相关的栈寄存器。

通常堆是大内存的时候使用,栈是小内存的时候使用。

所有权规则

首先,让我们看一下所有权规则。

  • Rust 中的每个值都有一个名为owner的变量。
  • 一次只能有一个所有者。
  • 当所有者离开作用域时,该内存将被删除(释放)。

变量的作用域

let s = "hello"
此时 "hello" 所有者是s,会在栈上分配空间。 他是被写死(hardcoded),到程序的text段(文本段)里面(程序文本段是存放不可更的数据和代码)。

为了更清晰的说明问题,我们要介绍一下String类型

上面定义的字符串,在使用的过程中不可以追加和修改。在实际操作的过程中,我们会利用字符串函数来生成可变的字符串。字符串除了内容,还有长度的标识。

字符串申请

let s = String::from("hello")
s.push_str(", world!"); //追加内容
此时字符串在堆上申请了空间,示意图如下:

2551644895709_.pic.jpg

如果此时我们执行下面的代码所有权将会发生怎样的变化呢?

   let s1 = String::from("hello");
   let s2 = s1;
所有权见图:

2561644897022_.pic.jpg

此时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);
这个操作是否一样,y拥有了5的所有权,x也不能使用了呢?

答案是: 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 并移出给调用的函数
}