# 何谓所有权

*所有权* 是掌管着 Rust 程序管理内存方式的一套规则（*ownership* is a set of rules that governs how a Rust program manages memory）。所有程序在其运行期间，都必须管理其运用计算机内存的方式。一些语言有着伴随着其程序运行，而持续查找不再用到内存的垃圾回收；在别的一些语言中，程序员必须显式地分配和释放内存。Rust 采用了第三条路线：经由带有编译器会加以检查的一套规则的所有权系统，内存便得到了管理。在这些规则的任何一条被违反了时，程序就不会编译。所有权的任何一个特性，都不会在程序运行期间，拖慢程序运行速度。

由于对许多程序员来说，所有权都是个新概念，因此要些时间来习惯他。好消息则是随着对 Rust 与那些所有权系统规则的愈加熟练，那么就会发现，顺其自然地开发出安全且高效的代码，会变得越来越容易。请务必坚持下去！

在掌握了所有权后，就会对那些令到 Rust 成为一门独特编程语言的特性，有扎实掌握。在本章中，将通过完成着重于甚为常见的一些数据结构：字符串的示例，而掌握到所有权。

> **内存栈与堆，the Stack and the Heap**
>
> 许多编程语言，都不要求进程考虑内存栈与堆。不过在像是 Rust 这样的系统编程语言中，某个值是在栈上还是在堆上，就会对语言的行为方式，造成影响，还会影响到不得不做出一些明确决定的理由。本章稍后将讲到的所有权的那些部分，是与内存栈和堆有关的，因此这里是关于他们的一点简要说明，作为预备知识。
>
> 内存栈和堆，都属于在运行时代码可用内存的组成部分，但他们是以不同方式架构组织起来的。栈，the stack 以其收到值的顺序，保存着一些值，并以相反的顺序，将这些值移除。这被成为 *后进先出，last in, first out*。设想有一叠盘子：在添加更多盘子时，就要把新的盘子放在盘子堆顶上，而在要用个盘子时，就要从顶上拿。从底下或中间添加或拿走盘子，都是不行的！添加数据被称为 “压入栈，pushing onto the stack”，而移除数据被称为 *弹出栈，popping off the stack*。保存在栈上的数据，必须要有已知的、固定的大小。相反，那些运行时未知大小，或大小可能会变化的数据，就必须保存在堆上。
>
> 内存堆的组织程度较低：在将数据放在堆上时，就要请求确切数量的空间。内存分配器会在堆上找到一处足够大的空白位点，将其标记为正在使用中，然后返回一个 *指针，pointer*，即那个点位的地址。此过程被称为 *堆上内存分配，allocating on the heap*，而有时会去掉“堆”，而简称为 *内存分配，allocating* （而将值压入到栈上，则不被视为内存分配）。由于到堆的指针是已知的、固定大小的，因此就可以将该指针存储在栈上，而在想要具体数据时，就必须依循该指针。请设想正坐在某个餐馆里。在进到餐馆时，就要报出跟你们组的人数，进而餐馆员工就会找出一张可以坐下所有人的空桌子，并把你们带过去。在你们组有人迟到时，他们就可以询问是坐在哪张桌子，而找到你们。
>
> 由于在把数据压到栈上时，内存分配器绝不必搜寻一个位置来存储新数据，因此相比在堆上分配空间，把数据压入栈是要快得多的；存储新数据的地方，始终是在栈顶部。与此相比，在内存堆上分配空间则需要更多工作，由于内存分配器必须先找到一块足够大空间来保存该数据，并随后还要为准备好下一次内存分配，而完成对此次分配的登记。
>
> 因为必须要循着某个指针去获取到数据，因此访问内存堆上的数据，与访问栈上的数据相比，也要慢一些。当较少地在内存中跳跃时，现代处理器会更快。延续上面的比喻，设想餐馆里的一名服务员，正在接收来自许多台餐桌的点餐。那么一次获取到一个桌子的全部点餐，再去往下一桌，无疑是最高效的。而从餐桌 A 拿到一份点餐，再从餐桌 B 拿到一份点餐，随后又从餐桌 A 拿到一份，然后又从餐桌 B 再拿到一份，这样无疑就是慢得多的过程了。经由同一令牌，如果处理器处理的数据与另一数据靠近（就像在栈上那样），而不是远离另一数据（就像在内存堆上可能的情形），那么处理器无疑会更好地完成他的工作。
>
> 在代码对某个函数进行调用时，传入到该函数的值（潜在包含了指向内存堆上数据的指针），以及该函数的本地变量，都是被压入到栈上的。在该函数结束运行后，这些值就被从栈上弹出。
>
> 对代码的哪些部分正在使用内存堆上的哪些数据进行追踪，最小化内存堆上的重复数据数量，以及对内存堆上的未使用数据进行清理而不至于耗尽内存空间等，都是所有权要解决的问题。一旦掌握了所有权，就再也不需要经常考虑栈和堆了，而清楚了所有权主要目的，是为着对内存堆进行管理，则会有助于解释所有权，为何会以他自己的方式运作。


## 所有权规则

首先，来看看这些所有权规则。在完成后面用于演示这些规则的示例时，请牢记这些规则：

- Rust 中的每个值，都有一个名为 *所有者，owner* 的变量；
- 同一时间，只能有一个所有者；
- 在其所有者超出作用域，scope 时，该值就被丢弃。


## 变量作用域

**Variable Scope**

既然已经学了 Rust 基础语法，接下来就不会在示例中，包含整个的 `fn main() {` 代码了，那么若跟随这些示例，就要确保把接下来的这些示例，自己手动放在 `main` 函数里头。这样的结果就是，这些示例会比较精炼一点，着重于具体细节而不是那些样板代码。

作为所有权的首个示例，这里将考察一下一些变量的 *作用域，scope*。作用域是指某个项目在程序中的有效范围。以下面这个变量来说：

```rust
let s = "hello";
```

这里的变量 `s` 指向一个字符串字面值，其中的字符串的值，则是被硬编码到这个程序的文本。自变量被声明处，到当前 *作用域* 结束处，变量都是有效的。下面清单 4-1 给出了一个带有对变量 `s` 在何处有效，进行注解注释的程序：

```rust
{                       // 变量 s 在这里是无效的，他还没被声明出来
    let s = "hello";    // s 自此往下都是有效的

    // 对变量 s 执行一些操作
}                       // 此时该作用域就结束了，而变量 s 也不再有效
```

*清单 4-1：变量与其间有效的作用域，a variable and the scope in which it is valid*

换句话说，这里有两个重点：

- 当变量 `s` 一旦来到作用域，他就有效了，when `s` comes *into scope*, it is valid；
- 他会保持有效，直到 *超出作用域*，it remains valid until it goes *out of scope*。


到这里，作用域和变量何时有效二者之间的关系，与其他语言中的此类关系类似。现在就要通过引入 `String` 类型，在此理解之上建构出所有权的理解，now we'll build on top of this understanding by introducing the `String` type。


## `String` 类型

为了对所有权的那些规则进行演示，就需要比前面第 3 章的 ["数据类型"](Ch03_Common_Programming_Concepts.md#数据类型) 小节中讲到那些类型，更为复杂一些的数据类型。前面讲到的那些类型，都是已知大小、可存储在栈上的，且在他们的作用域结束时会被弹出栈，在代码另一部分需要在不同作用域中用到同一值时，这些类型还可被快速而简单地复制，而构造出新的、独立实例。不过这里要审视的是存储在内存堆上的数据，进而探讨 Rust 是如何知晓，何时要清理这些内存堆上的数据，那么 `String` 类型就是极佳的示例了。

这里将着重于 `String` 类型与所有权有关的部分。这些方面同样适用于其他的、不论是由标准库还是自己创建的复合数据类型，complex data types。在 [第 8 章](Ch08_Common_Collections.md#何为-string) 将深入讲解 `String` 类型。

前面咱们已经见到了一些字符串字面值，其中有个硬编码到程序里的字符串值。字符串字面值很方便，但对于那些打算使用文本的全部情形，他们却并不适合。一个原因是字符串字面值为不可变的。另一个原因则是，在编写代码时，并非每个字符串的值都是已知的：比如，假设要获取用户输入并存储下来呢？对于这样的情形，Rust 有着第二种字符串类型，即 `String`。这种类型对分配到内存堆上的数据加以管理，并因此而具备了存储在编译时数量未知文本的能力。使用 `String` 类型的 `from` 函数，就可以从字符串字面值，创建出一个 `String` 类型的值来，如下所示：

```rust
let s = String::from("hello");
// 变量 s 的类型为：String, 而此前字面值中的变量 s 的类型为：&str
```

其中的双冒号（`::`）运算符，实现了将这个特定 `from` 函数，置于 `String` 类型的命名空间之下，而无需使用类似于 `string_from` 这种名字了。在第 5 章的 [方法语法](Ch05_Using_Structs_to_Structure_Related_Data.md#方法语法) 小节，并在第 7 章的 [对模组树中的某个项目进行引用的路径](Ch07_Managing_Growing_Projects_with_Packages_Crates_and_Modules.md#用于引用目录树中项目的路径) 小节，对模组命名空间的介绍中，将对这种语法进行更多讲解。

这种字符串，*能* 被改变：

```rust
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() 方法把字面值追加到某个字符串
println! ("{}", s); // 这将打印出 `hello, world!`
```

那么，到底字面值, `&str` 与 `String` 类型有何不同？为何 `String` 可以被改变，而字面值却不能？区别就在于，这两种类型处理内存的方式，是不同的。


## 内存与内存分配

对于字符串字面值这种情况，在编译时咱们就知道其内容，因此该文本就被直接硬编码到了最终的可执行文件。这就是为何字符串字面值快速高效的原因。然而这些属性，只是来源于字符串字面值的不可变性。不幸的是，对于那些在编译时大小未知的，且在运行期间大小可能改变的各个文本，是无法为他们而将某块内存，放入到二进制程序中的（unfortunately, we can't put a blob of memory into the binary for each piece of text whose size is unknown at compile time and whose size might change while running the program）。

在 `String` 类型下，为了支持可变、可增长的一段文本，就需要在内存堆上分配某个数量的内存，用来保存文本的那些内容，而这个数量在编译时则是未知的。这就意味着：

- 该内存必须在运行时向内存分配器请求；
- 在使用完那个 `String` 值之后，需要把这片内存交回给内存分配器的某种途径。

其中第一部分是由代码编写者完成的：在调用 `String::from` 时，这个 `from` 方法的实现，就请求了他所需的内存。在各种编程语言中，这是相当通行的做法。

然而，这第二部分就有所不同了。在带有 *垃圾收集器，garbage collector, GC* 的那些语言中，对那些不再是正被使用中的内存的追踪和清理，是由垃圾收集器完成的，对此这里无需去考虑。而在大多数不带垃圾收集器的语言，就要靠代码编写者自己，去识别内存在何时不再被使用，并像请求内存时一样，要调用代码来显式地释放他。要正确完成这样的内存释放，早已成为一个历史悠久的编程难题。若忘记了，咱们就将浪费内存。而过早地释放内存，则将造成变量失效。若执行两次，那也同样是程序错误。咱们需要严格地一个 `allocate` 对应一个 `free`。

Rust 采取了不同的路线：一旦某个变量超出了作用域，那么该变量所持有的内存空间，就被自动退回。下面是对清单 4-1 那个作用域示例，使用 `String` 而非字符串字面值的一个版本：

```rust
    {
        let s = String::from("hello");  // 变量 s 自此往下是有效的

        // 以变量 s 完成一些操作
    }                                   // 该作用域到此时结束，而变量 s
                                        // 不再有效
```

其中就存在可将那个 `String` 类型的值所需的内存，退回给内存分配器的一个天然时间点：即在变量 `s` 超出作用域时。在变量超出作用域时，Rust 就会主动调用一个特殊函数。该函数名为 `drop`，其正是 `String` 类型的编写者，放置用于内存退回的代码之处。在那个结束花括号处，Rust 会自动调用这个 `drop` 函数。

> 注意：在 C++ 中，在某程序中项目生命周期结束时，资源重分配的这种模式，有时被称为 *资源获取即初始化*（in C++, this pattern of deallocating resources at the end of an item's lifetime is sometimes called *Resource Acquisition Is Initialization, RAII*）。若曾用过 RAII 模式，那么 Rust 中的这个 `drop` 函数就会不那么陌生了。

这种模式对 Rust 代码编写方式有深远影响。在此刻他可能看起来还算简单，但在想要让多个变量，使用早先在内存堆上分配的数据，这种更为复杂情形时，代码行为就会无法被预见到。现在就来探讨一下一些这样的情况。


## 变量与数据互操作方式之一：迁移（所有权）

**Variables and Data Interacting with Move**



在 Rust 中，多个变量可以多种方式，与同一数据进行互操作。来看看下面清单 4-2 中用到整数的示例：

```rust
let x = 5;
let y = x;
```

*清单 4-2：将变量 `x` 的整数值，赋值给变量 `y`*

或许能猜到这段代码正在完成的事情：“把值 `5` 绑定到变量 `x`；随后构造一份 `x` 中值的拷贝并将其绑定到变量 `y`。” 现在就有了两个变量，`x` 与 `y`，且他们都等于 `5`。由于整数是有着已知的、固定大小的简单值，因此这实际上就是正在发生的事情，且这两个 `5` 的值都是被压入到栈上的。

> **注**：这就是下面会讲到的 [栈上数据的拷贝，copy](#唯栈数据拷贝stack-only-data-copy) 情形。


那么现在来看看 `String` 的版本：

```rust
let s1 = String::from("hello");
let s2 = s1;
```

这代码看起来与上面的非常相似，那么这里就可以假定其工作方式也是一样的：那就是，第二行将构造出一个 `s1` 中值的拷贝，并将该拷贝绑定到 `s2`。不过这并非真的是实际发生的样子。

> **注**：下面的代码将打印出 `s1 = 你好, s2 = 你好`，表示类型 `&str` （字符串切片）是存储在栈上的。

```rust
fn main() {
    let s1 = "你好";
    let s2 = s1;

    println! ("s1 = {}, s2 = {}", s1, s2);
}
```

请参阅下面的图 4-1，来搞明白在幕后 `String` 到底发生了什么。`String` 类型的值，是由三部分构成，在下图中的左边有给出：一个指向到保存该字符串内容内存的指针、一个长度，和一个该字符串的容量。这样一组数据被保存在栈上。下图的右边，即是内存堆上保存着字符串内容的内存。

![Rust 中 `String` 类型的本质](images/Ch04_01.svg)

*图 4-1：、保存着绑定到变量 `s1` 的值 `hello` 的一个 `String` 类型值在内存中的表示*

> **注**：`String` 类似属于 [灵巧指针，smart pointer](Ch15_Smart_Pointers.md)，他是个包含了指针与其他一些元数据的，带有一些方法的特别能力的结构体。

其中的长度，即为以字节计数、该 `String` 值内容正使用着的内存数量。而容量则是该 `String` 值从内存分配器处收到的、以字节计算的内存数量。长度与容量之间的区别，会相当重要，但在此情形下尚不重要，到目前未知，是可以忽略容量这个部分的。

在将 `s1` 赋值给 `s2` 时，这个 `String` 值被拷贝了，表示这里拷贝了栈上的指针、长度和容量。这里并未拷贝指针指向的、内存堆上的数据。也就是说，内存中数据的表示，如下图 4-2 所示：

![有着变量 `s1` 的指针、长度与容量拷贝的变量 `s2` 在内存中的表示](images/Ch04_02.svg)

*图 4-2：有着变量 `s1` 的指针、长度与容量拷贝的变量 `s2` 在内存中的表示*

这种表示 *不* 同于下图 4-3，那才是 Rust 对内存堆上的数据进行拷贝时，内存看起来的样子。如果 Rust 像下图 4-3 中那样做，那么当内存堆上的数据较大时， `s2 = s1` 的这个操作，将会在运行时性能开销上代价高昂。

![`s2 = s1` 操作的另一种可能：Rust 拷贝内存堆数据](images/Ch04_03.svg)

*图 4-3：`s2 = s1` 操作的另一种可能：Rust 同时拷贝内存堆数据*

早先曾讲过，在变量超出作用域后，Rust 会自动调用那个 `drop` 函数，而清理掉那个变量的堆内存。但图 4-2 则给出了两个指针都指向同一位置的情况。这就是个问题了：在 `s2` 与 `s1` 都超出作用域时，他们都将尝试去释放那同样的内存。这被称为 *双重释放，double free* 错误，是先前提到过的内存安全错误之一，one of the memory safety bugs。二次释放内存，可导致内存损坏，而内存损坏则会潜在导致安全漏洞。

为确保内存安全，Rust 在代码行 `s2 = s1` 之后，便不再认为 `s1` 是有效的了。因此，在 `s1` 超出作用域后，Rust 便不需要释放任何内存。下面就来检查一下，在 `s2` 创建出来后，去尝试使用 `s1` 会发生什么；这样做是不会工作的：

```rust
    let s1 = String::from("hello");  // 这里 s 的类型为：String
    let s2 = s1;

    println! ("{}", s1);
```

由于 Rust 阻止了对失效引用变量的使用，因此将收到一个下面这样的错误：

```console
$ cargo run
   Compiling string_demo v0.1.0 (/home/peng/rust-lang/projects/string_demo)
warning: unused variable: `s2`
 --> src/main.rs:3:9
  |
3 |     let s2 = s1;
  |         ^^ help: if this is intentional, prefix it with an underscore: `_s2`
  |
  = note: `#[warn(unused_variables)]` on by default

error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:5:21
  |
2 |     let s1 = String::from("hello");  // 这里 s 的类型为：String
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |
5 |     println! ("{}", s1);
  |                     ^^ value borrowed here after move
  |
  = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0382`.
warning: `string_demo` (bin "string_demo") generated 1 warning
error: could not compile `string_demo` due to previous error; 1 warning emitted
```

若在使用其他编程语言时，曾听说过 *浅拷贝（shallow copy）* 和 *深拷贝（deep copy）* 这两个说法，那么这种对指针、长度与容量的拷贝，而未拷贝数据的概念，或许听起来像是进行了一次浅拷贝。但由于 Rust 还将第一个变量进行了失效处理，因此这里就不叫浅拷贝，而叫做 *迁移（move）*。在这个示例中，就会讲，变量 `s1` 已被 *迁移* 到变量 `s2` 里了。因此真实发生的事情，就是下图 4-4 显示的那样：

![在变量 `s1` 失效后内存中的表示](images/Ch04_04.svg)

*图 4-4：在变量 `s1` 失效后内存中的表示*

这就解决了问题！在只有 `s2` 有效之下，当变量 `s2` 超出作用域后，那么就只有他会释放内存，于是就解决了双重内存释放问题。

此外，这种做法背后，还隐含着一种语言设计上的取舍：Rust 绝不会自动创建数据的 “深” 拷贝。由此，任何 *自动* 拷贝，都可认为在运行时性能开销上的影响很小（Therefore, any *automatic* copying can be assumed to be inexpensive in terms of runtime performance）。


## 变量与数据交互方式之二：克隆

**Variables and Data Interacting with Clone**



在 *确实* 打算对 `String` 的内存堆数据，而非只是栈数据进行深度拷贝时，就可以使用一个常用的、名为 `clone` 的方法。在第 5 章将讨论到方法语法，而由于在众多编程语言中，方法都是共同特性，那么此前大概率是见到过方法的。

下面是一个运作中的 `clone` 方法示例：

```rust
fn main() {
    let s1 = String::from("hello");  // 这里 s 的类型为：String
    let s2 = s1.clone();

    println! ("s1 = {}, s2 = {}", s1, s2);
}
```

这段代码工作起来毫无问题，并显式地产生出图 4-3 中给出的行为，其间内存堆数据确实得以拷贝。

当咱们看到对 `clone` 的调用时，咱们就知道一些任意的代码正在被执行，而这些代码可能开销很大。这是表明正在发生一些不同寻常事情的明显标志。


## 唯栈数据：拷贝

**Stack-Only Data: Copy**



尚有另一个至今还未讲到的小问题。正使用着整数的这段代码 -- 其中一部分在下面的清单 4-2 中给出了 -- 会工作并是有效代码：

```rust
let x = 5;
let y = x;

println! ("x = {}, y = {}", x, y);
```

然而这段代码，似乎与前面刚刚所掌握的相抵触：这里没有对 `clone` 的调用，但变量 `x` 依然有效，而并未迁移到变量 `y` 中去。

原因就在于，诸如整数这样的，在编译时大小已知的类型，都是被整个存储在栈上，那么构造他们具体值的拷贝是迅速的。那就意味着，在构造出变量 `y` 之后，就没有理由要去阻止变量 `x` 一直有效了。换句话说，此时的深拷贝与浅拷贝之间，是没有区别的，因此对 `clone` 进行调用，不会完成与通常的浅拷贝有任何区别的事情，进而就能忽略这个 `clone` 方法。

Rust 有着叫做 `Copy` 特质（the `Copy` trait, 在第 10 章将对特质，traits，进行更多的讲解）的，可放在像是整数这样的、存储于栈上的那些类型之上的一个特殊注解，a special annotation。在某个类型实现了 `Copy` 特质时，使用此类型的那些变量，就不会迁移，相反会轻而易举地被复制，从而在赋值给另一变量后，令到他们依然有效。

在某个类型或类型的任何部分带有 `Copy` 特质时，Rust 就不会再允许以 `Drop` 特质对其加以注解了。若某个类型需要在其值超出作用域后，还要进行某些特殊处理，而又将 `Copy` 注解添加到了那个类型，那么就会收到编译时错误（if the type needs something special to happen when the value goes out of scope and we add the `Copy` annotation to that type, we'll get a compile-time error）。要了解如何将 `Copy` 注解，添加到自己编写的类型而实现这个 `Copy` 特质，请参阅附录 C 中 [可派生特质（derivable traits）](Ch21_Appendix.md#附录-c派生特质)。

那么到底哪些类型要实现 `Copy` 特质呢？可查阅给定类型的文档，来确定相应类型是否有实现 `Copy` 特质，不过作为一般规则，任何组别的简单标量值，any group of simple scalar values，都可实现 `Copy` 特质，以及不要求分配内存堆分配，或者其他形式资源的类型，也都可以实现 `Copy` 特质（any group of simple scalar values can implement `Copy`, and nothing that requires allocation or is some form of resource can implement `Copy`）。下面就是一些实现 `Copy` 特质的类型：

- 全部的整型，比如 `u32`；
- 布尔值类型，`bool`，即值 `true` 与 `false`；
- 全部浮点数类型，比如 `f64`;
- 字符类型，`char`;
- 只包含实现 `Copy` 特质类型的元组类型。比如 `(i32, i32)` 这个元组类型，就实现了 `Copy` 特质，而 `(i32, String)` 则没有。


## 所有权与函数


将值传递给函数的语法，与将值赋值给变量的语法，是类似的。将变量传递给函数，就会进行迁移或拷贝，这与赋值所做的别无二致。下面的清单 4-3 有着一个带有一些注解的示例，对其中的变量进入和超出作用域，进行了展示。

文件名：`src/main.rs`


```rust
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` 方法就会被调用。some_string 的
    // 内存就被释放了。

fn makes_copy(some_integer: i32) {  // 变量 some_integer 进到作用域
    println! ("{}", some_integer);
}   // 到这里，变量 some_integer 超出作用域。没有特别事情发生。
```

*清单 4-3：带所有权与作用域注解的函数*

> 注：下面的代码，仍然会报出：`use of moved value: ``some_string```错误：

```rust
fn takes_ownership(some_string: String) {
    println! ("{}", some_string);
    another_takes_ownership(some_string);
    third_takes_ownership(some_string);
}
```

在对 `takes_ownership` 的调用之后，尝试使用变量 `s` 时，Rust 就会抛出一个编译时错误。这样的静态检查，保护咱们免于出错。请将使用变量 `s` 与变量 `x` 的代码，添加到 `main` 函数中，来观察一下在哪些地方可以使用他们，以及所有权规则会怎样阻止这样做。


## 返回值与作用域

**Return Values and Scope**


返回值也会转移所有权。下面的清单 4-4 给出了一个返回了某个值的函数示例，该示例有着与清单 4-3 中的那些类似的注释。

文件名：`src/main.rs`

```rust
fn main() {
    let s1 = gives_ownership();         // gives_ownership 将其返回值
                                        // 迁移到变量 s1 中

    let s2 = String::from("hello");     // 变量 s2 进入作用域

    let s3 = takes_and_gives_bake(s2);  // 变量 s2 被迁移到 takes_and_gives_back
                                        // 中，该函数又将他的返回值迁移到变量 s3 中

    println! ("{}, {}", s1, s3);
}   // 到这里，变量 s3 超出作用域而被丢弃。变量 s2 已被迁移，因此什么也不会发生。而
    // 变量 s1 则超出作用域而被丢弃。

fn gives_ownership() -> String {    // 函数 gives_ownership 将把他的返回值，迁移
                                    // 到调用他的函数中（即 main 函数）
    String::from("归你了")          // 此表达式的值将被返回，并迁出到调用函数
}

// 此函数接收一个 String 并要返回一个 String
fn takes_and_gives_bake(a_string: String) -> String {   // a_string 进入作用域
    a_string    // a_string 被返回，并迁出到调用函数
}
```

*清单 4-4：返回值的所有权转移*

变量所有权每次都依循同一模式：在将值赋给另一变量时，所有权就会迁移。包含着内存堆上数据的某个变量，在超出作用域时，除非数据所有权已被迁移至另一变量，否则该值就会被 `drop` 给清理掉。

而在此模式生效时，每个函数下的取得所有权与随后的交回所有权，就有点乏味了。在要某个函数使用某个值而不占据其所有权时，会怎样呢？如果希望再度使用传入到函数中的全部东西，并还要把他们和那些可能要返回的函数体运算结果，一起再传回来，那样就很烦人了。

如下面的清单 4-5 所示，Rust 确实允许使用一个元组，返回多个值：

文件名：`src/main.rs`

```rust
fn main() {
    let s1 = String::from("hello");

    let (s2, len): (String, usize) = calculate_length(s1);

    println! ("字符串 {} 的长度为：{}", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();

    (s, length)
}
```

*清单 4-5：返回参数所有权*

这虽然间接实现了消除变量所有权占据下，函数的使用变量，但对于这种本应常见的概念来说，这样做就过于花哨，且带来了大量工作负担。幸运的是，Rust 有着一项使用某个值而不转移所有权，名为 *引用（references）* 的特性。
