# 7.2 存储向量 (Storage Vectors)

The second collection type we’ll look at is `StorageVec<T>`. Just like vectors on the heap (i.e. `Vec<T>`), storage vectors allow you to store more than one value in a single data structure where each value is assigned an index and can only store values of the same type. However, unlike `Vec<T>`, the elements of a `StorageVec` are stored in _persistent storage_, and consecutive elements are not necessarily stored in storage slots that have consecutive keys.

我们要看的第二种集聚类型是`StorageVec<T>`。就像堆上的向量（即`Vec<T>`）一样，存储向量允许您在单个数据结构中存储多个值，其中每个值都分配有一个索引并且只存储相同类型的值。但是，与`Vec<T>` 不同的是，`StorageVec` 的元素存储在 _持久存储_ 中，并且连续的元素不一定存储在具有连续键的存储槽中。

In order to use `StorageVec<T>`, you must first import `StorageVec` as follows:

为了使用`StorageVec<T>`，您必须首先导入`StorageVec`，如下所示：

```
use std::storage::storage_vec::*;

```

Another major difference between `Vec<T>` and `StorageVec<T>` is that `StorageVec<T>` can only be used in a contract because only contracts are allowed to access persistent storage.

`Vec<T>` 和 `StorageVec<T>` 之间的另一个主要区别是 `StorageVec<T>` 只能在合约中使用，因为只有合约才允许访问持久存储。

### 创建一个新的存储向量 (Creating a New Storage Vector)

To create a new empty storage vector, we have to declare the vector in a `storage` block as follows:

要创建一个新的空存储向量，我们必须在 `storage` 块中声明该向量，如下所示：

```
    v: StorageVec<u64> = StorageVec {},

```

Just like any other storage variable, two things are required when declaring a `StorageVec`: a type annotation and an initializer. The initializer is just an empty struct of type `StorageVec` because `StorageVec<T>` itself is an empty struct! Everything that is interesting about `StorageVec<T>` is implemented in its methods.

就像任何其他存储变量一样，声明 `StorageVec` 时需要两件事：类型注释和初始化程序。初始化器只是一个 `StorageVec` 类型的空结构，因为 `StorageVec<T>` 本身是一个空结构！ `StorageVec<T>` 的所有有趣之处都在其方法中实现。

Storage vectors, just like `Vec<T>`, are implemented using generics which means that the `StorageVec<T>` type provided by the standard library can hold any type. When we create a storage vector to hold a specific type, we can specify the type within angle brackets. In the example above, we’ve told the Sway compiler that the `StorageVec<T>` in `v` will hold elements of the `u64` type.

存储向量，就像 `Vec<T>` 一样，是使用泛型实现的，这意味着标准库提供的 `StorageVec<T>`类型可以容纳任何类型。当我们创建存储向量来保存特定类型时，我们可以在尖括号内指定类型。在上面的示例中，我们已经告诉 Sway 编译器，`v` 中的`StorageVec<T>` 将保存 `u64` 类型的元素。

### 更新存储向量 (Updating a Storage Vector)

To add elements to a storage vector, we can use the `push` method, as shown below:

要将元素添加到存储向量，我们可以使用`push` 方法，如下所示：

```
    #[storage(read, write)]
    fn push_to_storage_vec() {
        storage.v.push(5);
        storage.v.push(6);
        storage.v.push(7);
        storage.v.push(8);
    }

```

Note two details here. First, in order to use `push`, we need to first access the vector using the `storage` keyword. Second, because `push` requires accessing storage, a `storage` annotation is required on the ABI function that calls `push`. While it may seem that `#[storage(write)]` should be enough here, the `read` annotation is also required because each call to `push` requires _reading_ (and then updating) the length of the storage vector which is also stored in persistent storage.

这里注意两个细节。首先，为了使用 `push`，我们需要先使用 `storage` 关键字访问向量。其次，因为 `push` 需要访问存储，所以调用 `push` 的 ABI 函数需要一个 `storage` 注释。虽然这里看起来 `#[storage(write)]` 应该足够了，但还需要 `read` 注释，因为每次调用 `push` 都需要 _读取_ （然后更新）存储向量的长度，这也是存储在持久存储中。

> **Note** The storage annotation is also required for any private function defined in the contract that tries to push into the vector.\
> **注意** 合约中定义的，试图推入向量的任何私有函数也需要存储注释。

> **Note** There is no need to add the `mut` keyword when declaring a `StorageVec<T>`. All storage variables are mutable by default.\
> **注意** 声明 `StorageVec<T>` 时无需添加 `mut` 关键字。默认情况下，所有存储变量都是可变的。

### 读取存储向量的元素 (Reading Elements of Storage Vectors)

To read a value stored in a vector at a particular index, you can use the `get` method as shown below:

要读取存储在特定索引处的向量中的值，您可以使用 `get` 方法，如下所示：

```
    #[storage(read)]
    fn read_from_storage_vec() {
        let third = storage.v.get(2);
        match third {
            Option::Some(third) => log(third.read()),
            Option::None => revert(42),
        }
    }

```

Note three details here. First, we use the index value of `2` to get the third element because vectors are indexed by number, starting at zero. Second, we get the third element by using the `get` method with the index passed as an argument, which gives us an `Option<StorageKey<T>>`. Third, the ABI function calling `get` only requires the annotation `#[storage(read)]` as one might expect because `get` does not write to storage.

这里注意三个细节。首先，我们使用 `2` 的索引值来获取第三个元素，因为向量是按数字索引的，从0开始。其次，我们通过使用 `get` 方法将索引作为参数传递来获取第三个元素，这为我们提供了 `Option<StorageKey<T>>`。第三，调用 `get` 的 ABI 函数只需要注释 `#[storage(read)]` 正如人们可能期望的那样，因为 `get` 不写入存储。

When the `get` method is passed an index that is outside the vector, it returns `None` without panicking. This is particularly useful if accessing an element beyond the range of the vector may happen occasionally under normal circumstances. Your code will then have logic to handle having either `Some(element)` or `None`. For example, the index could be coming as a contract method argument. If the argument passed is too large, the method `get` will return a `None` value, and the contract method may then decide to revert when that happens or return a meaningful error that tells the user how many items are in the current vector and give them another chance to pass a valid value.

当向 `get` 方法传递一个向量外部的索引时，它会返回 `None` 且不会出现恐慌。如果在正常情况下偶尔会访问超出向量范围的元素，这将特别有用。然后，您的代码将具有处理`Some(element)`或`None`的逻辑。例如，索引可以作为合约方法参数出现。如果传递的参数太大，方法 `get` 将返回一个 `None` 值，然后合约方法可能决定在这种情况发生时恢复或返回一个有意义的错误，告诉用户当前向量中有多少项并给他们另一次传递有效值的机会。

### 迭代向量中的值 (Iterating over the Values in a Vector)

To access each element in a vector in turn, we would iterate through all of the valid indices using a `while` loop and the `len` method as shown below:

要依次访问向量中的每个元素，我们将使用 while 循环和 len 方法遍历所有有效索引，如下所示：

```
    #[storage(read)]
    fn iterate_over_a_storage_vec() {
        let mut i = 0;
        while i < storage.v.len() {
            log(storage.v.get(i).unwrap().read());
            i += 1;
        }
    }

```

Again, this is quite similar to iterating over the elements of a `Vec<T>` where we use the method `len` to return the length of the vector. We also call the method `unwrap` to extract the `Option` returned by `get` followed by a call to `read()` to actually read the stored value. We know that `unwrap` will not fail (i.e. will not cause a revert) because each index `i` passed to `get` is known to be smaller than the length of the vector.

同样，这与迭代 `Vec<T>` 的元素非常相似，我们在其中使用`len` 方法返回向量的长度。我们还调用方法 `unwrap` 来提取 `get` 返回的 `Option` ，然后调用 `read()` 来实际读取存储的值。我们知道 `unwrap` 不会失败（即不会导致还原），因为已知传递给 `get` 的每个索引 `i` 都小于向量的长度。

### 使用穷举来存储多种类型 (Using an Enum to store Multiple Types)

Storage vectors, just like `Vec<T>`, can only store values that are the same type. Similarly to what we did for `Vec<T>` in the section [Using an Enum to store Multiple Types](https://fuellabs.github.io/sway/v0.38.0/book/common-collections/vec.html#using-an-enum-to-store-multiple-types), we can define an enum whose variants will hold the different value types, and all the enum variants will be considered the same type: that of the enum. This is shown below:

存储向量，就像 `Vec<T>` 一样，只能存储相同类型的值。类似于我们在\[使用穷举存储多种类型]\(https://fuellabs.github.io/sway/v0.38.0/book/common-collections/vec.html #using-an-enum-to-store-multiple-types)，我们可以定义一个穷举，其变体将包含不同的值类型，并且所有穷举变体将被视为同一类型：穷举的类型。如下所示：

```
enum TableCell {
    Int: u64,
    B256: b256,
    Boolean: bool,
}

```

Then we can declare a storage vector in a `storage` block to hold that enum and so, ultimately, holds different types:

然后我们可以在 `storage` 块中声明一个存储向量来保存该穷举，因此最终保存不同的类型：

```
    row: StorageVec<TableCell> = StorageVec {},

```

We can now push different enum variants to the storage vector as follows:

我们现在可以将不同的穷举变体推送到存储向量，如下所示：

```
    #[storage(read, write)]
    fn push_to_multiple_types_storage_vec() {
        storage.row.push(TableCell::Int(3));
        storage.row.push(TableCell::B256(0x0101010101010101010101010101010101010101010101010101010101010101));
        storage.row.push(TableCell::Boolean(true));
    }

```

Now that we’ve discussed some of the most common ways to use storage vectors, be sure to review the API documentation for all the many useful methods defined on `StorageVec<T>` by the standard library. For now, these can be found in the [source code for `StorageVec<T>`](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/storage.sw). For example, in addition to `push`, a `pop` method removes and returns the last element, a `remove` method removes and returns the element at some chosen index within the vector, an `insert` method inserts an element at some chosen index within the vector, etc.

现在我们已经讨论了使用存储向量的一些最常见方法，请务必查看 API 文档以了解标准库在 `StorageVec<T>` 上定义的所有许多有用方法。目前，这些可以在 [`StorageVec<T>` 的源代码](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/storage.sw) 中找到。例如，除了 `push` 之外，`pop` 方法删除并返回最后一个元素，`remove` 方法删除并返回向量中某个选定索引处的元素，`insert` 方法在某个位置插入一个元素向量中选择的索引等

### 嵌套存储向量 (Nested Storage Vecs)

It is possible to nest storage vectors as follows:

可以按如下方式嵌套存储向量：

```
    nested_vec: StorageVec<StorageVec<u64>> = StorageVec {},

```

The nested vector can then be accessed as follows:

然后可以按如下方式访问嵌套向量：

```
    #[storage(read, write)]
    fn access_nested_vec() {
        storage.nested_vec.push(StorageVec {});
        storage.nested_vec.push(StorageVec {});

        let mut inner_vec0 = storage.nested_vec.get(0).unwrap();
        let mut inner_vec1 = storage.nested_vec.get(1).unwrap();

        inner_vec0.push(0);
        inner_vec0.push(1);

        inner_vec1.push(2);
        inner_vec1.push(3);
        inner_vec1.push(4);

        assert(inner_vec0.len() == 2);
        assert(inner_vec0.get(0).unwrap().read() == 0);
        assert(inner_vec0.get(1).unwrap().read() == 1);
        assert(inner_vec0.get(2).is_none());

        assert(inner_vec1.len() == 3);
        assert(inner_vec1.get(0).unwrap().read() == 2);
        assert(inner_vec1.get(1).unwrap().read() == 3);
        assert(inner_vec1.get(2).unwrap().read() == 4);
        assert(inner_vec1.get(3).is_none());
    }

```
