# 使用矢量值存储值的清单

**Storing Lists of Values with Vectors**


这里要看的第一个集合，便是 `Vec<T>`，也叫做 *矢量（vector）* 类型。矢量类型允许将多个值，存储在单个的、将全部这些值挨个放入内存的数据结构中。矢量类型仅能存储同一类型的这些值。在有着某个数据项目清单，比如某个文件中的那些文本行，或购物车中那些货品价格时，那么矢量类型就是有用的。


## 创建一个新的矢量值

要创建出一个新的空矢量值，就要调用 `Vec::new()` 函数，如下清单 8-1 所示：

```rust
    let v: Vec<i32> = Vec::new();
```

*清单 8-1：创建一个新的、用于保持一些类型 `i32` 值的空矢量*

请注意这里添加了个类型注解。由于这里没有往这个矢量插入任何值，因此 Rust 是不清楚这里要存储何种类别元素的。这是个重点。矢量值是使用泛型实现的；在后面第 10 章中，就会讲到怎样在自己的类型中使用泛型。而此刻，就要明白由标准库提供的这个 `Vec<T>` 可以保存任何类型。在创建保存特定类型的矢量时，可在尖括号里头指定那个类型。在清单 8-1 中，就告诉了 Rust，`v` 中的那个 `Vec<T>` 将保存 `i32` 类型的元素。

而更为常见的则是，会创建带有初始值的 `Vec<T>`，同时 Rust 就会推断出要存储的值类型，那么就很少会进行这样的类型注解。Rust 贴心地提供了 `vec!` 这个宏，这个宏就会创建出一个新的、保存给到他的那些值的矢量来。下面清单 8-2 就创建了一个新的、保存了值 `1`、`2` 与 `3` 的 `Vec<i32>`。之所以那个整数类型为 `i32`，是由于 `i32` 正是默认的整数类型，如同第 3 章的 ["数据类型"](Ch03_Common_Programming_Concepts.md#数据类型) 中所讨论的那样。

```rust
let v = vec! [1, 2, 3];
```

*清单 8-2：创建一个新的包含了值的矢量*

由于这里已经给定了一些初始化 `i32` 的值，因此 Rust 就可以推断出 `v` 的类型为 `Vec<i32>`，而那个类型注解就不是必要的了。接下来，就要看看怎样修改矢量。


## 更新某个矢量值


要创建出一个矢量，并随后将一些元素添加给他，就可以使用 `push` 方法，如下清单 8-3 中所示。

```rust
    let mut v = Vec::new();

    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
```

*清单 8-3：使用 `push` 方法来把一些值添加到某个矢量*

正如第 3 章中所讨论的，这里与任何变量一样，在想要能修改矢量的值时，就要使用 `mut` 关键字，将其构造为可变。这里在矢量内部的数字，全部都是 `i32` 类型，而 Rust 就会从这些数据，推断出这个类型，因此这里不需要 `Vec<i32>` 类型注解。


## 读取矢量的元素

**Reading Elements of Vectors**


引用存储在矢量中某个值的方式有两种：经由索引，或使用 `get` 方法。在接下来的示例中，为讲得更清楚的原因，已经对从这些方法返回的值进行了注释。

下面清单 8-5 给出了访问矢量某个值的两种方式，即索引语法与 `get` 方法。

```rust
    let v = vec! [1, 2, 3, 4];

    let third: &i32 = &v[2];
    println! ("第三个元素为 {}", third);

    match v.get(2) {
        Some(third) => println! ("第三个元素为 {}", third),
        None => println! ("没有第三个元素。"),
    }
```

*清单 8-5：使用索引语法或 `get` 方法访问矢量中的某个元素*

请留意这里的两个细节。首先，由于矢量是以从零开始的数字进行索引，因此这里使用了索引值 `2` 来获取那第三个元素。其次，这里是通过同时使用 `&` 与 `[]`，获取第三个元素的，这就给到一个引用变量，而使用带有作为参数传递索引的 `get` 方法，给到的却是个 `Option<&T>` 值。

Rust 提供这两种引用某个元素方式的原因在于，有了这两种方式，就可以在尝试使用某个超出了既有元素范围的索引值时，对程序此时的表现加以选择。比如，下面就来看看在有着一个五个元素的矢量，而随后尝试以两种技巧，来访问位于索引 `100` 处元素时，会发生什么事情，如下清单 8-6 中所示。

```rust
    let v = vec! [1, 2, 3, 4, 5];

    let does_not_exist = &v[100];
    let does_not_exist = v.get(100);
```

*清单 8-6：尝试访问包含五个元素矢量中索引 `100` 处的元素*

在运行此代码时，由于第一种 `[]` 方式引用了不存在的元素，因此将导致程序死机。在有着某个对超出矢量末端的元素进行访问的尝试，而打算将程序崩溃掉时，那么用这种方式是最佳的。

而在传递给 `get` 方法的索引，位于矢量外部时，他就会返回不会程序死机的 `None` 值。在寻常情况下，就会时不时出现对超出矢量范围元素的访问时，就应使用这种方式。这时代码就将有如同第 6 章中所讨论的，处理 `Some(&element)` 或 `None` 值的逻辑。比如，那个索引可以是来自某人输入的数字。在他们不小心输入了一个过大的数字时，程序就会得到一个 `None` 值，这时就可以告诉用户在当前矢量中有多少个项目，并给到他们又一次输入有效值的机会。比起由于输入错误而将程序崩溃掉，这将是更加用户友好！

当程序有了有效引用时，Rust 的借用检查器，就会强制执行所有权与借用规则检查（在第 4 章讲到过），来确保该引用及全部其他的、到这个矢量内容的引用是有效的。请回顾那条表明了不能在同一作用域中，有着多个可变与不可变引用的规则。那条规则就适用于下面清单 8-7，清单中有着一个到矢量首个元素的可变引用，并尝试将一个元素添加到示例末尾。若同时在那个函数中尝试引用那个元素，那么该程序就不会工作：

```rust
    let mut v = vec! [1, 2, 3, 4, 5];

    let first: &i32 = &v[0];

    v.push(6);

    println! ("首个元素为：{}", first);
```

*清单 8-7：在保留到矢量某个条目的引用同时，尝试将一个元素添加到该矢量*

对此代码进行编译，将引发下面这个错误：

```console
$ cargo run                     ✔
   Compiling vec_demo v0.1.0 (/home/peng/rust-lang/projects/vec_demo)
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
 --> src/main.rs:6:5
  |
4 |     let first: &i32 = &v[0];
  |                        - immutable borrow occurs here
5 |
6 |     v.push(6);
  |     ^^^^^^^^^ mutable borrow occurs here
7 |
8 |     println! ("首个元素为：{}", first);
  |                                 ----- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `vec_demo` due to previous error
```

清单 8-7 中的代码看起来似乎可以工作：为何到矢量首个元素的引用，会牵连到该矢量末尾的变化呢？这个报错是由于矢量工作原理：由于矢量是将他的那些值，挨个放在内存中的，那么将新元素添加到矢量末尾，而在该矢量当前存储处，没有足够场所来挨个放置全部这些元素时，这时就会要求分配新内存，并将那些旧有元素拷贝到新内存空间。在那种情况下，到首个元素的引用就会指向已解分配内存（deallocated memory）。而正是这些 Rust 的借用规则，防止程序已这样的情形而告终。

> **请注意**：更多有关 `Vec<T>` 类型的实现细节，请参考 [Rust 专论（The Rustonomicon）](https://doc.rust-lang.org/nomicon/vec/vec.html)。


## 迭代矢量中的值

**Iterating over the Values in a Vector**


要依次访问矢量中的各个元素，就要迭代全部元素，而非使用那些索引值，一次访问一个了。下面清单 8-8 展示了怎样使用 `for` 循环，来获取到一个 `i32` 矢量值中各个元素的不可变引用，并将这些元素打印出来。

```rust
    let v = vec! [100, 32, 57];

    for i in &v {
        println! ("{}", i);
    }
```

*清单 8-8：通过使用 `for` 循环对各个元素进行迭代，而打印出矢量中的每个元素*

也可以为了对全部元素进行修改，而对可变示例中的各个元素，进行可变引用的迭代。下面清单 8-9 中的 `for` 循环，将把 `50` 添加到各个元素。

```rust
    let mut v = vec! [100, 32, 57];

    for i in &mut v {
        *i += 50;
    }
```

*清单 8-9：对实例中各个元素，进行可变引用的迭代*

要修改可变引用所指向的值，就必须使用 `*` 解引用操作符（the `*` dereference operator），在能够使用 `+=` 运算符之前，获取到 `i` 中的那个值。在后面第 15 章的 [“以解引用操作符，顺着指针找到值”](Ch15_Smart_Pointers.md#跟随指针到其值) 小节，就会讲到这个解引用操作符。


## 运用枚举存储多种类型

**Using an Enum to Store Multiple Types**


矢量只能存储同一类型的值。这就会不方便；显然是有需要存储不同类型条目清单的使用场景。幸运的是，枚举的那些变种，就是定义在同一枚举类型之下的，那么在需要某种表示那些不同类型元素的类型时，就可以定义并使用一个枚举！

好比说这里要从电子表格的某行，其中该行的一些列包含了整数，另一些列包含了浮点数，而其他列则包含了字符串，而要从这行获取到这些数据。那么就可以定义这样一个枚举，他的那些变种将保存这些不同值类型，而全部这些变种，就会被看着是同一类型：即为该枚举。随后就可以创建一个矢量，来保存那个枚举，进而最终保存了这些不同类型。下面清单 8-10 中就对此进行了演示。

```rust
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }

    let row = vec! [
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
```

*清单 8-10：定义一个 `enum` 来在矢量中存储不同类型的值*

Rust 需要在编译时了解那个矢量中会有些什么类型，这样他就清楚存储该矢量的每个元素，所需要的内存堆上内存准确数量。同时必须显式声明该矢量中允许哪些类型。若 Rust 允许矢量保存任意类型，那么就会存在一个或多个类型，将引发在该矢量的元素上执行操作错误的可能。运用枚举加上 `match` 表达式，就意味着 Rust 将在编译时，确保所有可能情形都被处理，如同第 6 章中讨论的那样。

但若不清楚运行时程序会在矢量中收到的详尽类型集合，那么这个枚举技巧就不会有用。相反，这个时候就可以使用特质对象（a trait object），而在后面的第 17 章就会讲到特质对象。

既然这里已经讨论了使用矢量的一些最常用方式，那就一定要看看 [API 文档](https://doc.rust-lang.org/std/vec/struct.Vec.html)，了解那些定义在 `Vec<T>` 上，由标准库所定义的全部有用方法。比如，除了 `push` 之外，`pop` 方法会移除并返回矢量的最后一个元素。


## 丢弃某个矢量，就会丢弃他的元素

**Dropping a Vector Drops Its Elements**


与其他任何 `struct` 一样，矢量在超出作用域时，就会被释放掉，如下清单 8-4 所示。

```rust
    {
        let v = vec! [1, 2, 3, 4];

        // 对 v 执行一些操作
    } // 这里 v 就超出了作用域，而被释放掉
```

*清单 8-4：对矢量及其元素在何处被丢弃进行展示*

在这个矢量被丢弃时，那么他所有内容也会被丢弃，即他保存的那些整数将被清理掉。这初一看似乎直接明了，然而在开始触及到一些到该矢量元素的引用时，事情就会变得复杂。接下来就要解决这个问题！


下面就移步到下一种集合类型：`String` 吧!

