> For the complete documentation index, see [llms.txt](https://chapin666.gitbook.io/rust-crash-course-zh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chapin666.gitbook.io/rust-crash-course-zh/6.-sheng-ming-zhou-qi-he-qie-pian/su-cheng-ban-di-6-ke.md).

# 速成课第6节

到目前为止，我们已经介绍了生命周期和值序列话的一些细节。 现在是时候深入研究并正确了解生命周期和切片了。

这篇文章是基于 [FP](https://www.fpcomplete.com/rust) 完成 Rust 教学系列的一部分。 如果你在博客之外阅读这篇文章，你可以在[介绍文章的顶部](https://www.snoyman.com/blog/2018/10/introducing-rust-crash-course)找到这个系列中所有文章的链接。 也可[订阅 RSS](https://www.snoyman.com/feed/rust-crash-course) 频道。

## 打印 person

让我们来看看一些相当平常的代码：

```rust
#[derive(Debug)]
struct Person {
    name: Option<String>,
    age: Option<u32>,
}

fn print_person(person: Person) {
    match person.name {
        Some(name) => println!("Name is {}", name),
        None => println!("No name provided"),
    }

    match person.age {
        Some(age) => println!("Age is {}", age),
        None => println!("No age provided"),
    }
}

fn main() {
    print_person(Person {
        name: Some(String::from("Alice")),
        age: Some(30),
    });
}
```

相当简单，这是一个模式匹配的很好的示范。 但是，让我们再加上一行。 尝试在 print\_person 函数的开头添加以下内容：

```rust
println!("Full Person value: {:?}", person);
```

一切正常。 先打印 person 的全部内容，然后是模式匹配。 但是尝试在函数末尾添加上这行，你会得到一个编译错误：

```rust
error[E0382]: use of partially moved value: `person`
  --> main.rs:18:41
   |
9  |         Some(name) => println!("Name is {}", name),
   |              ---- value moved here
...
18 |     println!("Full Person value: {:?}", person);
   |                                         ^^^^^^ value used here after move
   |
   = note: move occurs because the value has type `std::string::String`, which does not implement the `Copy` trait

error: aborting due to previous error
```

> 注意：这是我使用的Rust编译器1.30.1的错误。 但是，已经有计划来改善这种情况。

问题在于我们已经消耗了 person 的一部分，因此无法打印它。 我们可以通过再次设置来解决此问题。 让 person 参数可变，然后使用默认的None 值填充移动的 person.name：

```rust
fn print_person(mut person: Person) {
    match person.name {
        Some(name) => println!("Name is {}", name),
        None => println!("No name provided"),
    }

    match person.age {
        Some(age) => println!("Age is {}", age),
        None => println!("No age provided"),
    }

    person.name = None;

    println!("Full Person value: {:?}", person);
}
```

编译运行，但现在输出结果令人困惑：

```rust
Name is Alice
Age is 30
Full Person value: Person { name: None, age: Some(30) }
```

注意最后一行中name的值是None，理想情况下应该是 Some (Alice)。 我们可以做得更好，通过返回 match 中的 name：

```rust
person.name = match person.name {
    Some(name) => {
        println!("Name is {}", name);
        Some(name)
    },
    None => {
        println!("No name provided");
        None
    }
};
```

但是这样做是不雅的。 让我们后退一步。 我们真的需要 消费/move person.name  吗？ 也不尽然。 它应该按照引用来做所有的事情。 因此，让我们回过头来，通过使用一个借词来完全避免这种移动：

```rust
match &person.name {
    Some(name) => println!("Name is {}", name),
    None => println!("No name provided"),
}
```

好多了！ 不过我们不需要在 person.age 上引用，因为 u32 是可复制的。 在这里，我们对一个引用进行了模式匹配分析，因此这个 name 也是一个引用。

但是，我们可以使用 ref 关键字更明确地说明这一点。 这个关键词表明，当模式匹配时，我们希望模式是一个引用，而不是原始值的移动。 (更多请参考[ 《in the Rust book](https://doc.rust-lang.org/book/second-edition/ch18-03-pattern-syntax.html#creating-references-in-patterns-with-ref-and-ref-mut)》) 。我们最终得到的是：

```rust
match person.name {
    Some(ref name) => println!("Name is {}", name),
    None => println!("No name provided"),
}
```

在我们的例子中，这与 \&person.name 的基本结果相同。

## Birthday!

让我们修改代码，以便在打印 age 时也将 age 增加1。首先是下面的问题，代码不能编译通过，请尝试预测原因：

```rust
match person.age {
    Some(age) => {
        println!("Age is {}", age);
        age += 1;
    }
    None => println!("No age provided"),
}
```

我们正在尝试更改局部 age 的绑定，但这是不可变的。 好吧，这很容易修复，只需将Some(age)替换为 Some(mut age)。 可以编译，但带有警告：

```rust
warning: value assigned to `age` is never read
  --> src/main.rs:16:13
   |
16 |             age += 1;
   |             ^^^
   |
   = note: #[warn(unused_assignments)] on by default
```

然后输出是：

```
Name is Alice
Age is 30
Full Person value: Person { name: Some("Alice"), age: Some(30) }
```

注意最后一行，age 仍然是30岁，而不是31岁。 花一分钟时间试着理解这里发生了什么... 完成了吗？ 酷。

1. 根据 person.age 模式匹配
2. 如果是Some，我们需要将 age 移动到局部 age
3. 但是由于类型是 u32，它将创建一个副本并移动该副本
4. 当我们递增 age 时，我们递增了一个从未使用过的副本

我们可以通过引用 person.age 来解决这个问题：

```rust
fn print_person(person: Person) {
    match person.name {
        Some(ref name) => println!("Name is {}", name),
        None => println!("No name provided"),
    }

    match &mut person.age {
        Some(age) => {
            println!("Age is {}", age);
            age += 1;
        }
        None => println!("No age provided"),
    }

    println!("Full Person value: {:?}", person);
}
```

编译器抱怨：age是一个 \&mut u32，但是我们试图在其上使用 +=  ：

```rust
error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut u32`
  --> src/main.rs:16:13
   |
16 |             age += 1;
   |             ---^^^^^
   |             |
   |             cannot use `+=` on type `&mut u32`
   |
   = help: `+=` can be used on 'u32', you can dereference `age`: `*age`
```

根据编译器的踏实，我们需要采用取消年龄引用。 但又出现一个错误：

```rust
error[E0596]: cannot borrow field `person.age` of immutable binding as mutable
  --> src/main.rs:13:16
   |
7  | fn print_person(person: Person) {
   |                 ------ consider changing this to `mut person`
...
13 |     match &mut person.age {
   |                ^^^^^^^^^^ cannot mutably borrow field of immutable binding

error: aborting due to previous error
```

同样，编译器准确地告诉我们如何解决这个问题: 使 person 可变。 继续，做出改变，一切都会顺利进行。

### 练习1

对于 person.name，我们提出了两种解决方案: 借用 person.name 或使用 ref 关键字。 同样的两种解决方案也适用于我们当前的问题。 我们刚刚演示了借用方法。 尝试使用 ref 关键字来解决这个问题。

## 单一迭代器

让我们做一个傻乎乎的小迭代器，它只产生一个值。 我们将通过使用 Option 来追踪是否产生了值：

```rust
struct Single<T> {
    next: Option<T>,
}
```

让我们创建一个辅助函数来创建单个值：

```rust
fn single<T>(t: T) -> Single<T> {
    Single {
        next: Some(t),
    }
}
```

让我们编写一个 main 函数来测试这个函数是否正常工作：

```rust
fn main() {
    let actual: Vec<u32> = single(42).collect();
    assert_eq!(vec![42], actual);
}
```

如果你试图编译它，你会得到一个错误消息：

```rust
error[E0599]: no method named `collect` found for type `Single<{integer}>` in the current scope
  --> src/main.rs:12:39
   |
1  | struct Single<T> {
   | ---------------- method `collect` not found for this
...
12 |     let actual: Vec<u32> = single(42).collect();
   |                                       ^^^^^^^
   |
   = note: the method `collect` exists but the following trait bounds were not satisfied:
           `&mut Single<{integer}> : std::iter::Iterator`
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `collect`, perhaps you need to implement it:
           candidate #1: `std::iter::Iterator`
```

为了使用collect()，我们需要提供一个 Iterator 实现。 Item 将会是 T。我们已经有了一个很好的 Option ，可以用于 next() 函数的返回值：

```rust
impl<T> Iterator for Single<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        self.next
    }
}
```

不幸的是，这并不奏效：

```rust
error[E0507]: cannot move out of borrowed content
  --> src/main.rs:21:9
   |
21 |         self.next
   |         ^^^^ cannot move out of borrowed content

error: aborting due to previous error
```

哦，对。 我们不能将结果值移出，因为我们的 next 函数只能可变借用 self。 让我们尝试一些模式匹配:

```rust
match self.next {
    Some(next) => Some(next),
    None => None,
}
```

除此之外，这还涉及从引用中移动，因此失败。 让我们再试一次：我们将进入一个局部变量，将 self.next 设置为 None（这样就不会再次重复该值），然后返回该局部变量：

```rust
fn next(&mut self) -> Option<T> {
    let res = self.next;
    self.next = None;
    res
}
```

不，编译器仍然不满意！ 我想我们只需要放弃对单一迭代器的宏伟愿景。 我们当然可以作弊：

```rust
fn next(&mut self) -> Option<T> {
    None
}
```

但是，尽管编译完成，但它在运行时未能通过我们的测试：

```rust
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `[42]`,
 right: `[]`', src/main.rs:13:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
```

## Swap

上面我们所做的是尝试将 self.next 与局部变量交换。 但是，借用检查员并不喜欢我们采用的方法。 但是，标准库有一个辅助函数std::mem::swap，该函数可以为我们提供帮助。 看起来像：

```rust
pub fn swap<T>(x: &mut T, y: &mut T)
```

毫无疑问，我们可以用它来解决我们的问题：

### 练习2

上面的代码不能完全编译，但是编译器可以指导您找到正确的解决方案。 试着找出上面的问题并自己解决它们。 如果做不到这一点，请求编译器帮助你。

## replace 和 take

你是否觉得创建临时变量这个东西有点冗长？ 是的，对于 Rust 标准库的作者来说也是如此。 有一个 helper 函数绕过了这个临时变量：

```rust
fn next(&mut self) -> Option<T> {
    std::mem::replace(&mut self.next, None)
}
```

好多了！ 但这应该是很容易的事情，这似乎仍然需要更多工作。幸运的是，对于 Rust 标准库的作者来说也是如此。 **使用 None 替换 Option 中的值，然后使用原始值**，这种模式非常常见，以至于他们给了它一个名称和一个方法: take。

```rust
fn next(&mut self) -> Option<T> {
    self.next.take()
}
```

我们完成了！

## 生命周期

我们在前面的课程中已经简要地提到了生命，但是现在是时候更加认真地对待它们了。 让我们来看看引用的一个简单用法：

```rust
struct Person {
    name: String,
    age: u32,
}

fn get_name(person: &Person) -> String {
    person.name
}

fn main() {
    let alice = Person {
        name: String::from("Alice"),
        age: 30,
    };
    let name = get_name(&alice);
    println!("Name: {}", name);

```

这段代码不能编译。 我们的 get\_name 函数接受一个 Person 的引用，然后尝试在其结果中移动这个 Person 的 name。 这不可能。 一个解决方案是克隆这个name：

```rust
fn get_name(person: &Person) -> String {
    person.name.clone()
}
```

虽然这样做是有效的，但是效率相对较低。 我们喜欢尽量避免复制。 相反，让我们可以简单地返回一个名称的引用：

```rust
fn get_name(person: &Person) -> &String {
    &person.name
}
```

万岁！ 但是让我们把函数变得更复杂一点。 现在我们需要一个函数，它将接受两个 Person，并返回 age 较大的 Person 的 name。 这听起来相当容易写：

```rust
struct Person {
    name: String,
    age: u32,
}

fn get_older_name(person1: &Person, person2: &Person) -> &String {
    if person1.age >= person2.age {
        &person1.name
    } else {
        &person2.name
    }
}

fn main() {
    let alice = Person {
        name: String::from("Alice"),
        age: 30,
    };
    let bob = Person {
        name: String::from("Bob"),
        age: 35,
    };
    let name = get_older_name(&alice, &bob);
    println!("Older person: {}", name);
}
```

不幸的是，编译器非常生我们的气：

```rust
error[E0106]: missing lifetime specifier
 --> src/main.rs:6:58
  |
6 | fn get_older_name(person1: &Person, person2: &Person) -> &String {
  |                                                          ^ expected lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `person1` or `person2`
```

该错误消息非常明显。 我们的函数是返回借用的值。 该值必须从某个地方借来。 仅有的两个选项是 person1 和 person2。 似乎 Rust 由于某种原因需要知道这一点。

> 这是一个小故障，请参阅下面的 “静态生命周期”。

还记得关于引用的规则吗？ 引用的生命周期不能超过它们的原始值。 我们需要跟踪结果值允许存活的时间，它必须小于或等于它来自生命的时间。 整个概念就是生命。

处于某种原因，我们稍后将要讨论(在“生命周期省略”之中)。通常我们可以使用显式生命周期绕过这个问题。 然而，有时候我们确实需要表达清楚。 为此，我们引入了一些新的参数。 但是这一次，它们是生存期参数，以单引号开头，小写。 通常它们只是一个单一的字母。 例如：

```rust
fn get_older_name<'a, 'b>(person1: &'a Person, person2: &'b Person) -> &String
```

我们仍然从编译器得到一个错误，因为我们的返回值没有生命周期。 我们应该选择 a 还是 b？ 或者我们可以创建一个新的 c 并尝试一下？ 让我们从 “ a” 开始。 我们得到了错误消息：

```rust
error[E0623]: lifetime mismatch
  --> src/main.rs:10:9
   |
6  | fn get_older_name<'a, 'b>(person1: &'a Person, person2: &'b Person) -> &'a String {
   |                                                         ----------     ----------
   |                                                         |
   |                                                         this parameter and the return type are declared with different lifetimes...
...
10 |         &person2.name
   |         ^^^^^^^^^^^^^ ...but data from `person2` is returned here
```

这是有道理的: 因为我们的结果可能来自于 person2，我们不能保证 ‘ a 寿命参数小于或等于 ’ b 寿命参数。 幸运的是，我们可以明确地声明，就像我们声明类型实现了一些 traits 一样:

```rust
fn get_older_name<'a, 'b: 'a>(person1: &'a Person, person2: &'b Person) -> &'a String {
```

这实际上可以编译！ 或者，在这种情况下，我们可以完全绕过第二个生命周期参数，并说 person1 和 person2 必须具有相同的生命周期，该生命周期必须与返回值相同：

```rust
fn get_older_name<'a>(person1: &'a Person, person2: &'a Person) -> &'a String {
```

如果你像我一样，你可能会认为这样做过于局限。 例如，我一开始认为上面的签名不能编译这段代码:

```rust
fn main() {
    let alice = Person {
        name: String::from("Alice"),
        age: 30,
    };
    foo(&alice);
}

fn foo(alice: &Person) {
    let bob = Person {
        name: String::from("Bob"),
        age: 35,
    };
    let name = get_older_name(&alice, &bob);
    println!("Older person: {}", name);
}
```

Alice 的生命周期显然比Bob的生命周期要长。 但是，函数签名中生存期的语义是所有值至少具有相同的生存期。 如果他们碰巧活得久一点，没有伤害，没有犯规。

## 多生命周期参数需求

那么，我们能否举出一个绝对需要多个生存期参数的例子呢? 当然！

```rust
fn message_and_return(msg: &String, ret: &String) -> &String {
    println!("Printing the message: {}", msg);
    ret
}

fn main() {
    let name = String::from("Alice");
    let msg = String::from("This is the message");
    let ret = message_and_return(&msg, &name);
    println!("Return value: {}", ret);
}
```

这段代码不能编译，因为我们需要一些生存期参数。 所以让我们用上面的方法，使用相同的参数：

```rust
fn message_and_return<'a>(msg: &'a String, ret: &'a String) -> &'a String {
```

可以编译，但是让我们的调用代码更加复杂：

```rust
fn main() {
    let name = String::from("Alice");
    let ret = foo(&name);
    println!("Return value: {}", ret);
}

fn foo(name: &String) -> &String {
    let msg = String::from("This is the message");
    message_and_return(&msg, &name)
}
```

现在编译器不高兴了：

```rust
error[E0597]: `msg` does not live long enough
  --> src/main.rs:14:25
   |
14 |     message_and_return(&msg, &name)
   |                         ^^^ borrowed value does not live long enough
15 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 12:1...
  --> src/main.rs:12:1
   |
12 | / fn foo(name: &String) -> &String {
13 | |     let msg = String::from("This is the message");
14 | |     message_and_return(&msg, &name)
15 | | }
   | |_^
```

我们已经说过，返回值必须与 msg 参数使用相同的生命周期。 但我们将返回值返回到 foo 函数之外，而 msg 值的生命周期不会超出 foo 的结尾。

调用代码应该没问题，我们只需要告诉编译器 msg 参数的生存期短于返回值就可以。

### 练习3

修改 message\_and\_return 的签名，以便代码编译和运行。

## 省略生命周期

为什么有时我们跳过生命周期，有时又需要包括它们？ 语言中有规则，称为“省略生命周期”。 与其试图自己解决这个问题，不如参考一下文档：

<https://doc.rust-lang.org/nomicon/lifetime-elision.html>

## 静态生命周期

上面，我暗示如果返回一个引用，那么它必须具有与其一个输入参数相同的生存期。这在大多数情况下是有道理的，因为否则我们将不得不凭空想出一些任意的生命周期。然而，这也是一个谎言。 有一个特殊的生命周期存在于整个程序中，叫做 'static。这是一个有趣的消息：自我们共同编写的第一个 Hello World 以来，您就暗中使用了它！

每个字符串实际上都是一个具有 static 生命周期的引用。

```rust
fn name() -> &'static str {
    "Alice"
}

fn main() {
    println!("{}", name());
}
```

## 数组、切片、Vec和字符串

这是另一个我们一直厚颜无耻的地方。 String 和 str 之间的区别是什么？ 这两个词都出现了不少。 我们一会儿会讲到这些。 首先，我们需要讨论数组、切片和向量。

### 数组

据我所知，关于数组的最好的官方文档是 API 文档本身。 数组是包含固定长度的单一数据类型的连续内存块。 类型表示为 \[ t; n ] ，其中 t 是值的类型，n 是数组的长度。 与任何正常的编程语言一样，数组在 Rust 中的索引是从0开始的。

初始化数组有两种语法，列出常量语法(如 Javascript、 Python 或 Haskell) :

```rust
fn main() {
    let nums: [u32; 5] = [1, 2, 3, 4, 5];
    println!("{:?}", nums);
}
```

和重复表达式：

```rust
fn main() {
    let nums: [u32; 5] = [42; 5];
    println!("{:?}", nums);
}
```

你可以让数组可以变，然后，对它们进行修改：

```rust
fn main() {
    let mut nums: [u32; 5] = [42; 5];
    nums[2] += 1;
    println!("{:?}", nums);
}
```

很好，但是如果您需要动态的东西怎么办？ 为此，我们有……

### Vec

Vec是 “ 连续的，可增长的数组类型”。 可以使用push或pull，可以在 O(1) 时间通过索引访问其长度。 我们还有一个漂亮的vec！ 用于构建它们的宏：

```rust
fn main() {
    let mut v: Vec<u32> = vec![1, 2, 3];
    v.push(4);
    assert_eq!(v.pop(), Some(4));
    v.push(4);
    v.push(5);
    v.push(6);
    assert_eq!(v.pop(), Some(6));
    assert_eq!(v[2], 3);
    println!("{:?}", v); // 1, 2, 3, 4, 5
}
```

### 切片

我将编写一个 helper 函数，它将输出 Vec 中的所有值：

```rust
fn main() {
    let v: Vec<u32> = vec![1, 2, 3];
    print_vals(v);
}

fn print_vals(v: Vec<u32>) {
    for i in v {
        println!("{}", i);
    }
}
```

当然，因为这是一个传递值，所以下面的代码不能编译：

```rust
fn main() {
    let mut v: Vec<u32> = vec![1, 2, 3];
    print_vals(v);
    v.push(4);
    print_vals(v);
}
```

很容易修复：让 print\_vals 引用一个 Vec:

```rust
fn main() {
    let mut v: Vec<u32> = vec![1, 2, 3];
    print_vals(&v);
    v.push(4);
    print_vals(&v);
}

fn print_vals(v: &Vec<u32>) {
    for i in v {
        println!("{}", i);
    }
}
```

不幸的是，这并不能泛化为，比如说，数组:

```rust
fn main() {
    let a: [u32; 5] = [1, 2, 3, 4, 5];
    print_vals(&a);
}
```

这个方法失败了，因为 print\_vals 需要一个 \&Vec\<u32> ，但是我们提供了一个 &\[u32; 5] 。 但是这是相当令人失望的。 一个动态Vec和一个固定长度的数组在很多情况下都是一样的。 有什么东西能把这两者都概括起来吗？

引入切片。 引用《The Rust book》：

***允许您引用集合中的连续元素序列，而不是整个集合。***

为了实现这些功能，我们需要将 print\_vals 的签名改&#x4E3A;***：***

```rust
fn print_vals(v: &[u32]) {
```

&\[u32] 是对切片 u32 的引用。 可以从数组或Vec创建切片，更不用说其他情况了。 （我们将稍微讨论＆借位运算符的作用）。作为一般性建议，如果您收到的是一个由一系列值组成的参数，请尝试使用切片，因为它会给调用者更好地控制数据的来源。

我在上面玩了一些文字游戏，在“引用切片”和“切片”之间切换。 显然，我们正在使用引用。 我们可以取消对切片引用的引用并获取切片本身吗？ 我们试试吧！

```rust
fn print_vals(vref: &[u32]) {
    let v = *vref;
    for i in v {
        println!("{}", i);
    }
}
```

编译器又跟我们发脾气了：

```rust
error[E0277]: the size for values of type `[u32]` cannot be known at compilation time
 --> src/main.rs:7:9
  |
7 |     let v = *vref;
  |         ^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `[u32]`
  = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-sized>
  = note: all local variables must have a statically known size

error[E0277]: the size for values of type `[u32]` cannot be known at compilation time
 --> src/main.rs:8:14
  |
8 |     for i in v {
  |              ^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `[u32]`
  = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-sized>
  = note: required by `std::iter::IntoIterator::into_iter`

error[E0277]: the trait bound `[u32]: std::iter::Iterator` is not satisfied
 --> src/main.rs:8:14
  |
8 |     for i in v {
  |              ^ `[u32]` is not an iterator; maybe try calling `.iter()` or a similar method
  |
  = help: the trait `std::iter::Iterator` is not implemented for `[u32]`
  = note: required by `std::iter::IntoIterator::into_iter`
```

基本上，没有办法取消对切片的引用。 从逻辑上讲，无论是在堆栈，堆还是在可执行文件本身上，只要保留对包含值的内存块的引用（例如字符串字面值，我们将在后面介绍），就合乎逻辑。

## Deref

有些地方不太对劲， 为什么 ampersand/borrow 操作符给我们提供了不同的类型！

```rust
fn main() {
    let v = vec![1, 2, 3];
    let _: &Vec<u32> = &v;
    let _: &[u32] = &v;
}
```

事实证明，借用运算符与 “ Deref强制” 交互。 如果您对此感到好奇，请查看 [Deref 特性的文档](https://doc.rust-lang.org/std/ops/trait.Deref.html)。 例如，我可以创建一个可以借用到切片中的新结构：

```rust
use std::ops::Deref;

struct MyArray([u32; 5]);

impl MyArray {
    fn new() -> MyArray {
        MyArray([42; 5])
    }
}

impl Deref for MyArray {
    type Target = [u32];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn main() {
    let ma = MyArray::new();
    let _: &MyArray = &ma;
    let _: &[u32] = &ma;
}
```

感谢 udoprog [回答了这个问题](https://stackoverflow.com/questions/53250856/can-i-borrow-a-custom-type-into-a-slice)。另外，仅仅因为你能做到这一点[并不意味着你就应该这么做](https://stackoverflow.com/questions/45086595/is-it-considered-a-bad-practice-to-implement-deref-for-newtypes)。

## 使用切片

切片与其他类型一样是数据类型。您可以查看[ std::slice 模块文档](https://doc.rust-lang.org/std/slice/index.html)和[ slice 原语类型](https://doc.rust-lang.org/std/primitive.slice.html)。

一些常见的互动方式包括：

* 使用它们作为迭代器
* 使用 slice\[idx] 语法对它们进行索引
* 使用 slice\[ start .. end ] 语法获取子切片

## Byte 字面量

如果在字符串文本前面放置一个小写 b，就会得到一个字节数组。 你可以把它看作一个固定长度的数组，或者，更常见的，看作一个切片：

```rust
fn main() {
    let bytearray1: &[u8; 22] = b"Hello World in binary!";
    let bytearray2: &[u8] = b"Hello World in binary!";
    println!("{:?}", bytearray1);
    println!("{:?}", bytearray2);
}
```

请注意，您总是接收到对值的引用，而不是值本身。 数据存储在程序可执行文件中，因此无法修改(因此总是接收不可变引用)。

### 练习4

在上面的 bytearray1 和 bytearray2 类型中添加生存期参数。

## Strings

最后我们可以谈谈字符串！ 您可能认为字符串文本是一个固定长度的字符数组。 事实上，你可以创造这样一个东西:

```rust
fn main() {
    let char_array: [char; 5] = ['H', 'e', 'l', 'l', 'o'];
    println!("{:?}", char_array);
}
```

但是，这不是 str。 上面的表述是非常低效的。 由于 Rust 中的 char 具有完全的 Unicode 支持，所以它占用了4个内存字节(32位)。

&#x20;然而，对于大多数数据来说，这是过度杀伤。 像 UTF-8这样的字符编码将会更有效率。

> 注意：如果您不熟悉Unicode字符编码，可以在此处忽略这些详细信息。 了解字符串在Rust中的工作方式并不是至关重要的。

取而代之的是，字符串切片(\&str) 本质上是围绕字节切片(&\[u8]) 的新型包装，该字节包装保证采用UTF-8编码。 这需要权衡一些重要事项：

* 您可以很便宜地(自由地?)从一个 \&str 转换为一个 &\[u8] ，这对于进行系统调用非常有用
* 您无法在字符串中获得 O(1)随机访问，因为 UTF-8 编码不允许这样做。 相反，您需要使用字符迭代器来查看单个字符。

### 练习5

在 String 上使用 std::env::args 和 chars() 方法打印出每个命令行参数中的字符数。 附加说明: 还要打印出字节数。 样本用法：

```rust
$ cargo run שלום
arg: target/debug/foo, characters: 16, bytes: 16
arg: שלום, characters: 4, bytes: 8
```

不要忘记，第一个参数是可执行文件的名称。

## 结构体的生命周期

今天的最后一个主题是结构体的生命周期。 在结构体中保留引用是完全可能的。 然而，当你这样做的时候，你需要清楚地说明他们的生命周期。 例如，这将无法编译：

```rust
struct Person {
    name: &str,
    age: u32,
}
```

相反，你需要这样写：

```rust
struct Person<'a> {
    name: &'a str,
    age: u32,
}
```

我收到的一般建议是，尽可能避免这种情况，我也会转达这个建议。 在处理结构体的生存期参数时，事情最终会变得非常复杂。 通常，应该在结构体中使用属性版本的值(例如，String 而不是 \&str，Vec 或数组而不是 slice)。 在这种情况下，您需要确保结构体引用的生存期超过结构本身的生存期。

有时候，如果在结构体中使用引用，就可以避免一些额外的克隆和分配，而当您需要这样做时，可能就会出现这种情况。 但是我建议你等到你的分析指出是瓶颈时再说。 欲了解更多信息，请参阅《[The Rust Book](https://doc.rust-lang.org/book/second-edition/ch10-03-lifetime-syntax.html#lifetime-annotations-in-struct-definitions)》。

## Api 中的引用和切片

以下是我收到的一些建议，大多数情况下都是正确的：

**接收参数时，尽可能选择切片**

然而有时候这是过于简单化的。 如果你想深入了解，有一篇很棒的博客文章介绍了公共 api 的一些权衡：[On dealing with owning and borrowing in public interfaces](https://phaazon.net/blog/on-owning-borrowing-pub-interface) ，[Reddit discussion](https://www.reddit.com/r/rust/comments/9tzygo/on_dealing_with_owning_and_borrowing_in_public/) 也很棒。
