# 什么是所有权？

所谓 *所有权，ownership*，属于一套规则，控制 Rust 程序管理内存方式。所有程序在运行时都必须管理他们使用计算机内存的方式。一些语言有着垃圾回收，在其运行时定期查找不再使用的内存；在别的语言中，程序员则必须显式分配及释放内存。Rust 使用第三种方法：在一套编译器会检查的规则下，内存经由所有权系统得以管理。当违反任何规则时，程序将不会编译。所有权的任何特性都不会在咱们程序运行时，减慢其运行速度。

因为所有权对于许多程序员来说都是个新概念，所以确实需要一些时间适应。好消息是，咱们对 Rust 与所有权系统规则越有经验，咱们就将发现自然而然地开发出安全高效代码就越容易。请坚持下去！

当咱们掌握所有权后，咱们将为掌握那些令到 Rust 独特的功能打下了坚实基础。在这一章中，咱们将通过一些示例了解所有权，这些示例重点关注极为常见的数据结构：字符串。


<a name="diff_between_stack_n_heap"></a>
> **内存栈与内存堆**
>
> 许多编程语言都不要求咱们经常考虑栈与堆。但在像 Rust 这样的系统编程语言中，某个值是在栈上还是在堆上会影响语言的行为方式，以及为何咱们必须做出某些决定的原因。这一章稍后的所有权的一些部分，将与栈和堆结合讲解，因此这里是个预备简要说明。
>
> 栈和堆都属于咱们代码在运行时可用的内存部分，但他们的结构方式不同。栈以其获取到值的顺序存储值，并以相反顺序移除值。这就是所谓的 *后进先出，last in, first out*。请设想一摞盘子：当咱们添加更多盘子时，就会把他们放在这堆盘子的顶部；当咱们需要一个盘子时，就从顶部取一个。从中间或底部添加或移除盘子的效果并不好！添加数据称为 *压入栈，pushing onto the stack*，移除数据称为 *弹出栈，popping off the stack*。存储于栈上的所有数据，都必须有着已知、固定大小。在编译时大小未知，或大小可能改变的数据必须存储在堆上。
>
> 堆的组织程度较低：当咱们将数据放入堆上时，咱们会请求一定数量的空间。内存分配器会在堆中找到一个足够大的空位，将其标记为在用，并返回一个 *指针，pointer*，即那个位置的地址。这个过程称为 *在堆上分配，allocating on the heap*，有时简称 *分配，allocating*（将值压入栈不被视为分配）。因为指向堆的指针是已知、固定大小，所以咱们可将指针存储在栈上，但是当咱们需要具体数据时必须跟随指针。设想在餐厅等待安排座位的情景。当咱们进门时，咱们会说明咱们团体的人数，然后接待员会找到一张能座下每个人的空桌并把咱们领到那里。当咱们团队中有人来晚了，他们可以询问咱们被安排座位于何处以找到咱们。
>
> 压入栈比在堆上分配更快，因为分配器永远不必搜索存储新数据的位置；该位置始终是在栈的顶部。相比之下，在堆上分配空间需要更多的工作，因为分配器必须首先找到一个足够大的空间来容纳数据，然后进行簿记，为下一次分配做准备。
>
> 访问堆中的数据一般慢于访问栈上的数据，因为咱们必须顺着指针才能到达那里。若现代处理器在内存中的跳转更少，那么他们就会更快。继续那个比喻，设想一名餐厅服务员在收取许多餐桌上的菜单。先处理一张桌子上的所有点餐，然后再处理下一张桌子上的点餐是最高效的。收取餐桌 A 一个菜单，然后 B 桌的一个菜单，然后再是 A 桌的一个菜单，然后再是 B 桌的一个菜单，将是慢得多的一个过程。出于同样原因，当处理器处理靠近其他数据，正如栈上那样，而不是较远的数据，正如堆上那样时，那么他通常就能更好地完成工作。
>
> 当咱们的代码调用函数时，传入函数的值（可能包括指向堆上数据的指针）和函数的局部变量会被压入栈。在函数结束后，这些值会从栈上弹出。
>
> 跟踪代码的哪些部分正在使用堆上的哪些数据、最大限度减少堆上的重复数据量，以及清理堆上的未使用数据以免空间耗尽等，这些都是所有权要解决的问题。一旦咱们掌握所有权，咱们就不需要经常考虑栈和堆。而知道所有权的主要目的是管理堆数据，有助于解释为什么他会这样工作。


## 所有权规则

首先，我们来看看所有权规则。在我们举例说明他们时，请牢记这些规则：

- Rust 中的每个值都有个 *所有者，owner*；
- 同一时间只能有一个所有者；
- 当所有者超出作用域时，该值将被丢弃。


## 变量作用域

既然我们已经掌握了基本的 Rust 语法，我们将不在示例中包含所有 `fn main() {` 代码，所以若咱们在跟进学习，那么请务必手动将以下示例放入 `main` 函数中。如此，我们的示例将更加简洁，让我们专注于具体细节而不是样板代码了。

作为所有权的首个示例，我们将探讨一些变量的作用域。所谓 *作用域，scope*，是程序中某个项目有效的范围。请看以下这个变量：


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

这个变量 `s` 指向一个字符串字面值，其中字符串的值被硬编码到我们程序的文本中。这个变量从其被声明处便有效，直到当前作用域结束。下面清单 4-1 展示了一个带有注释的程序，注释了变量 `s` 的于何处有效。


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

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

**清单 4-1**：变量及其有效的作用域


换句话说，这里有两个重要的时间点：


- 当 `s` *进入* 作用域时，他便有效了；
- 他会保持有效知道他 *超出作用域*。


此时，作用域与变量何时有效之间的关系，与其他编程语言中的类似。现在我们将通过引入 `String` 类型，构建于这种理解的基础上。


## `String` 类型

为了说明所有权规则，我们需要一种比第 3 章的 [数据类型](../programming_concepts/data_types.md) 小节中介绍的数据类型更复杂的数据类型。前面介绍的类型都是已知大小的，可存储在栈上，并会在其作用域结束时从栈上弹出，并当代码的另一部分需要在不同作用域下使用同一个值时，可被快速而简单地复制以创建一个新的独立实例。但我们打算看看存储在堆上的数据，并探讨 Rust 如何知道何时清理这些数据，而 `String` 类型就是个很好的例子。

我们将重点关注 `String` 中与所有权相关的部分。这些方面也适用于其他复杂数据类型，无论他们是由标准库提供还是由咱们创建出的。我们将在 [第 8 章](../common_collections/strings.md) 中讨论 `String` 的非所有权方面。

我们已经见过字符串的字面值，其中字符串值被硬编码到咱们程序中。字符串字面值很方便，但他们并不适合我们可能打算使用文本的所有情况。原因之一是他们属于不可变的。另一个原因是，当我们编写代码时，并非每个字符串值都是已知的：例如，若我们打算获取用户输入并存储他怎么办？正是针对这些情况，Rust 才有了 `String` 类型。这种类型管理分配于堆上的数据，因此能够存储编译时我们未知的大量文本。咱们可以使用 `from` 函数从字符串字面值创建出 `String`，如下所示：


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


其中双冒号 `::` 操作符允许我们将这个特定的 `from` 函数，命名空间到 `String` 类型下，而不是使用像是 `string_from` 的某种名字。我们将在第 5 章的 [方法语法](../structs/method_syntax.md) 小节，及第 7 章的 [用于引用模块树中某个项目的路径](../packages_crates_and_modules/paths.md) 小节讨论模块的命名空间时，进一步讨论这种语法。

这种字符串 *可* 被改变：

```rust
    let mut s = String::from("hello");

    s.push_str(", world!"); // push_str() 方法会追加一个字面值，到某个 String

    println! ("{}", s); // 这将打印出 `hello, world!`
```


那么，这里有何区别呢？为什么 `String` 可被改变而字面值却不能呢？区别在于这两种类型处理内存的方式不同。


## 内存与内存分配

在字符串字面值的情况下，我们在编译时知道内容，因此文本被直接硬编码到最终的可执行文件中。这就是字符串字面值快速且高效的原因。但这些属性仅来自于字符串字面值的不变性。不幸的是，我们无法为在编译时大小未知，且在运行程序时大小可能改变的每段文本，都添加一块内存到二进制程序中。

在 `String` 类型下，为了支持某段可变、可增长的文本片段，我们需要在堆上分配某一数量（在编译时未知）的内存来保存内容。这意味着：


- 这一内存必须在运行时请求自内存分配器；
- 在我们使用我们的 `String` 完毕后，我们需要一种方法将这一内存归还给分配器。


第一部分由我们完成：当我们调用 `String::from` 时，其实现会请求其所需的内存。这在编程语言中非常普遍。

然而，第二部分则有所不同。在带有 *垃圾回收器，garbage collector，GC* 的语言中，GC 会跟踪并清理不再使用的内存，我们不需要考虑他。在大多数没有 GC 的语言中，我们有责任识别内存何时不再被使用，并调用代码显式释放他，就像我们请求他一样。正确做到这一点，历来是编程难题。当我们忘记了了，我们将浪费内存。当我们过早进行时，我们将有着无效变量。当我们做了两次时，那也是个 bug。我们需要准确地将一次 `allocate` 与一次 `free` 配对。

Rust 采取了不同路径：一旦拥有内存的变量超出作用域，内存就会自动归还。下面是清单 4-1 中咱们作用域示例的一个版本，使用 `String` 而非字符串字面值：


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

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


其中有个我们可将 `String` 所需的内存归还给分配器的天然时间点：当 `s` 超出作用域时。当变量超出作用域时，Rust 会为我们调用一个特殊函数。这个函数叫做 `drop`，其便是 `String` 作者可放置归还内存代码之处。Rust 会在闭合大括号处自动调用这个 `drop` 函数。


> **注意**：在 C++ 中，这种在项目生命周期结束时解分配（释放）资源的模式，有时称为 *资源获取即初始化，Resource Acquisition Is Initialization，RAII*。若咱们曾用到 RAII 模式，那么 Rust 中的 `drop` 函数对咱们一定不会陌生。


这种模式对 Rust 代码的编写方式有着深刻影响。其现在看来可能很简单，但在一些更为复杂的情形下，当我们打算让多个变量使用我们在堆上分配的数据时，代码的行为可能会出乎意料。现在我们来探讨一下其中的一些情况。


### 变量与数据相互作用：迁移

在 Rust 中，多个变量可以不同的方式与同一数据交互。下面清单 4-2 中展示了个使用整数的示例。


<a name="listing_4-2"></a>
```rust
let x = 5;
let y = x;
```

**清单 4-2**：指派变量 `x` 的整数值给 `y`

我们大致可以猜到这段代码在做什么： “绑定值 `5` 到 `x`；然后构造 `x` 中值的拷贝并绑定其到 `y`”。我们现在有两个变量 `x` 和 `y`，且都等于 `5`。这确实是正在发生的事情，因为整数属于有着已知、固定大小的简单值，且这两个值 `5` 都会被压入栈上。


> **译注**：这就是下面会讲到的 [唯栈数据：拷贝](#唯栈数据拷贝) 情形。


现在我们来看看 `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: {s1}, s2: {s2}");
> }
> ```


请参阅图 4-1 了解 `String` 在幕后到底发生了什么。`String` 值由三部分组成，如左侧所示：

- 指向存放字符串内容内存的指针
- 长度
- 及容量


这组数据存储在栈上。而右侧是堆上保存着内容的内存。


<a name="f_4-1"></a>
![Rust 中 `String` 类型的本质](../images/Ch04_01.svg)

**图 4-1**：保存绑定到 `s1` 值 `"hello"` 的 `String` 值，在内存中的表示

> **译注**：`String` 类型类似属于 [灵巧指针](../Ch15_Smart_Pointers.md)，他是保存着指针与其他一些元数据，有着一些方法特别能力的结构体。


所谓长度，是 `String` 内容当前使用的内存量，以字节为单位。而容量是 `String` 已从内存分配器收到的内存总量，以字节为单位。长度与容量之间的差异很重要，但在这一语境下并不重要，所以暂时忽略容量即可。

当我们指派 `s1` 给 `s2` 时，`String` 数据会被复制，这意味着我们拷贝了全部在栈上的指针、长度及容量。我们不会拷贝指针指向的堆上数据。换句话说，内存中的数据表示如下图 4-2 所示。


<a name="f_4-2"></a>
![变量 `s2`  在内存中的表示，他有着 `s1` 的指针、长度与容量](../images/Ch04_02.svg)

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

这种表示 *不* 像是下图 4-3，其是当 Rust 也拷贝了堆数据时内存看起来的样子。若 Rust 真这样做了，那么当堆上的数据很大时，`s2 = s1` 这一操作在运行时性能方面就会非常昂贵。

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

*图 4-3：若 Rust 也复制了堆数据，那么 `s2 = s1` 可能会执行的另一种可能性*

早先，我们曾说过，当变量超出作用域时，Rust 会自动调用 `drop` 函数并清理该变量的堆内存。但图 4-2 显示两个数据指针都指向同一位置。这是一个问题：当 `s2` 和 `s1` 超出作用域时，他们都将尝试释放同一内存。这成为 *双重释放，double free* 错误，是我们之前提到的内存安全 bug 之一。释放两次内存会导致内存损坏，这会潜在导致安全漏洞。

为了确保内存安全，在 `let s2 = s1;` 行后，Rust 便将 `s1` 当作不再有效。而因此，在 `s1` 超出作用域时，Rust 不需要释放任何东西。请查看咱们在 ` s2` 创建后尝试使用 `s1` 时会发生什么；他不会工作：


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

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


咱们将得到如下错误，因为 Rust 会阻止咱们使用已失效的引用：


```console
$ cargo run
   Compiling ownership v0.1.0 (/home/hector/rust-lang-zh_CN/projects/ownership)
error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:5:29
  |
2 |     let s1 = String::from("hello");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |
5 |     println! ("{}, world!", s1);
  |                             ^^ value borrowed here after move
  |
  = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
  |
3 |     let s2 = s1.clone();
  |                ++++++++

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)]` (part of `#[warn(unused)]`) on by default

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

若咱们在使用其他语言时听说过 *浅拷贝，shallow copy* 与 *深拷贝，deep copy* 这两个术语，那么拷贝指针、长度和容量，而不拷贝数据的这一概念，听起来可能像在构造一份浅拷贝。但由于 Rust 还会使第一个变量失效，所以这不叫浅拷贝，而成为一次 *迁移，move*。在这个示例中，我们会说 `s1` 已被迁移到 `s2` 中。因此，实际发生的情况如下图 4-4 中所示。


<a name="f_4-4"></a>
![变量 `s1` 无效后内存中的表示](../images/Ch04_04.svg)

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


这解决了我们的问题！在只有 `s2` 有效下，当他超出作用域时，就只有他将释放内存，我们就大功告成了。

此外，这还暗含着一种设计取舍： Rust 将绝不自动创建咱们数据的 “深” 拷贝。因此，任何 *自动* 拷贝在运行时性能方面，都可认为成本很低。


### 作用域与赋值

作用域、所有权，与通过 `drop` 函数释放内存之间关系的反面同样成立。当咱们将某个全新值指派给一个既有变量时，Rust 将立即调用 `drop` 并释放原始值的内存。例如考虑以下代码：

```rust
    let mut s = String::from("hello");
    s = String::from("ahoy");

    println!("{s}, world!");
```

我们一开始声明一个变量 `s` 并将其绑定到值为 `"hello"` 的一个 `String`。随后我们立即以值 `"ahoy"` 创建一个新的 `String`，并将其指派给 `s`。此时，根本没有任何东西指向堆内存中的原始值。下图 4-5 演示了现在的栈与堆数据：

<a name="f_4-5"></a>
![初始值被完全替换后内存中的表示](../images/trpl04-05.svg)

**图 4-5**：初始值被完全替换后内存中的表示

原先的字符串因此立即超出作用域。Rust 将对其运行 `drop` 函数，其内存将立即释放。当我们最后打印该值时，其将是 `"ahoy, world!"`。


### 变量与数据相互作用：克隆

当我们 *确实* 打算深度拷贝 `String` 的堆数据，而不仅是栈数据时，我们可使用名为 `clone` 的常用方法。我们将在第 5 章讨论方法语法，但由于方法属于许多编程语言中的通用特性，因此咱们大概以前就已经见过。

下面是 `clone` 方法的一个实际应用示例：


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

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


这段代码工作良好，并会显式地产生图 4-3 中所示的行为，其中堆数据 *确实* 得以拷贝。

当咱们看到对 `clone` 的调用时，咱们就知道一些任意代码正被执行，而这些代码可能开销高昂。这是一些不同寻常事情正在发生的直观指示器。


### 唯栈数据：拷贝

还有一个咱们尚未讨论的细节。下面这段使用整数的代码 -- 部分已在清单 4-2 中给出 -- 会工作并时有效的：


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

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


但这段代码似乎与我们刚了解内容相矛盾：我们没有调用 `clone`，但 `x `仍然有效且未被迁移到 `y` 中。

原因是诸如整数这类在编译时有着已知大小的类型完全存储在栈上，所以要构造这些具体值的拷贝很快。这意味着我们没有理由在创建变量 `y` 后阻止 `x` 保持有效。换句话说，这里的深拷贝和浅拷贝没有区别，所以调用 `clone` 不会执行任何与通常的浅拷贝不同的事情，我们可以不使用他。

Rust 有个称为 `Copy` 特质的特殊注解，我们可以将其放置存储于栈中的类型上，就像整数一样（我们将在 [第 10 章](../generic_types_traits_and_lifetimes/traits.md) 详细讨论特质）。当某个类型实现 `Copy` 特质时，那么使用他的变量不会迁移，而是被简单地拷贝，从而使他们指派给另一变量后仍然有效。

当类型或其任何部分已经实现了 `Drop` 特质时，Rust 将不会让我们以 `Copy` 注解该类型。当类型需要在值超出作用域时发生一些特殊的事情（译注：`drop` 函数中执行代码），且我们添加 `Copy` 注解到该类型时，我们将得到一个编译时报错。要了解如何添加 `Copy` 注解添加到咱们的类型以实现特质，请参阅附录 C 中的 [派生特质](../appendix/derivable_traits.md)。

那么，哪些类型实现了 `Copy` 特质呢？咱们可查看给定类型的文档来确定，但作为一般规则，任何一组简单标量值都可以实现 `Copy`，而任何要求内存分配或某种形式资源的类型都无法实现 `Copy`。下面是一些实现 `Copy` 的类型：

- 所有整数类型，比如 `u32`；
- 布尔类型，`bool`，取值为 `true` 及 `false`；
- 所有浮点类型，比如 `f64`；
- 字符类型 `char`；
- 元组，当其只包含同时实现 `Copy` 的类型时。比如，`(i32, i32)` 实现了 `Copy`，但 `(i32, String)` 则没有。


## 所有权与函数

传递值到函数的机制与赋值给变量时的机制类似。传递变量给函数将迁移或拷贝，就像复制所做的那样。下面清单 4-3 有个示例，带有一些显示变量于何处进入与超出作用域的注释。


<a name="listing_4-3"></a>
文件名：`src/main.rs`

```rust
fn main() {
    let s = String::from("hello");  // s 进入作用域

    takes_ownership(s);             // 变量 s 的值迁移到这个函数中......
                                    // ......进而在这里不再有效

    let x = 5;                      // x 进入作用域

    makes_copy(x);                  // 由于 i32 实现了 `Copy` 特质，
                                    // x 不会迁移到这个函数中，
                                    // 因此后面使用 x 没有问题
    println! ("x = {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 超出作用域。没有特别事情发生。
```

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

若我们试图在调用 `takes_ownership` 后使用 `s`，Rust 将抛出一个编译时错误。这些静态检查保护我们免于犯错。请尝试添加使用 `s` 和 `x` 的代码到 `main` 中，看看咱们可于何处使用他们，以及所有权规则会在哪里会阻止咱们这样做。

> **译注**：下面的代码，仍然会报出：`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);
}
```

## 返回值与作用域

返回值也会转移所有权。下面清单 4-4 显示了个返回某个值的函数示例，带有与清单 4-3 中类似的注释。


<a name="listing_4-4"></a>
文件名：`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: {s1}, s3: {s3}");
}   // 这里，s3 超出作用域而被丢弃。变量 s2 已被迁移，因此什么也不会发生。
    // s1 超出作用域而被丢弃。

fn gives_ownership() -> String {                // gives_ownership 将把迁移其返回值
                                                // 到调用他的函数中

    let some_string = String::from("归你了");   // some_string 进到作用域

    some_string                                 // some_string 被返回并
                                                // 迁出到调用函数
}

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

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


变量的所有权每次都会遵循同一模式：赋值到另一变量会迁移他。当包含堆上数据的变量超出作用域时，该值将被 `drop` 清理，除非数据的所有权已迁移到另一变量。

虽然这种模式可行，但在每个函数中都取得所有权然后返回所有权会有些繁琐。若我们想要让函数使用值却不取得所有权，该怎么办呢？非常烦人的时，当我们希望再次使用我们传入的任何数据，以及我们可能希望返回的函数主体产生的任何数据，都需要被传回。

Rust 确实允许咱们使用元组返回多个值，如下清单 4-5 中所示。


<a name="listing_4-5"></a>
文件名：`src/main.rs`

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

    let (s2, len) = calculate_length(s1);

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

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

    (s, length)
}
```

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

但对于一个本应常见的概念来说，这太过繁琐且属于大量工作。幸运的是，Rust 有一项可在无需转移所有权下使用值的特性：*引用，references*。


（End）


