# 7.3 存储映射 (Storage Maps)

Another important common collection is the storage map. The type `StorageMap<K, V>` from the standard library stores a mapping of keys of type `K` to values of type `V` using a hashing function, which determines how it places these keys and values into _storage slots_. This is similar to [Rust's `HashMap<K, V>`](https://doc.rust-lang.org/std/collections/struct.HashMap.html) but with a few differences.

另一个重要的公共集合是存储映射。标准库中的类型 `StorageMap<K, V>` 使用哈希函数存储类型 `K` 的键到类型 `V` 的值的映射，这决定了如何将这些键和值放入 _存储槽_。这类似于 [Rust 的 HashMap\<K, V>](https://doc.rust-lang.org/std/collections/struct.HashMap.html)，但有一些差异。

Storage maps are useful when you want to look up data not by using an index, as you can with vectors, but by using a key that can be of any type. For example, when building a ledger-based sub-currency smart contract, you could keep track of the balance of each wallet in a storage map in which each key is a wallet’s `Address` and the values are each wallet’s balance. Given an `Address`, you can retrieve its balance.

当您不想像使用向量那样使用索引来查找数据，而是使用可以是任何类型的键来查找数据时，存储映射很有用。例如，在构建基于分类帐的子货币智能合约时，您可以在存储映射中跟踪每个钱包的余额，其中每个键是钱包的`Address 地址`，值是每个钱包的余额。给定一个 `Address`，您可以检索其余额。

Similarly to `StorageVec<T>`, `StorageMap<K, V>` can only be used in a contract because only contracts are allowed to access persistent storage.

与`StorageVec<T>` 类似，`StorageMap<K, V>` 只能在合约中使用，因为只有合约才允许访问持久存储。

`StorageMap<T>` is included in the [standard library prelude](https://fuellabs.github.io/sway/v0.38.0/book/introduction/standard_library.html#standard-library-prelude) which means that there is no need to import it manually.

`StorageMap<T>` 包含在 [标准库前置](https://fuellabs.github.io/sway/v0.38.0/book/introduction/standard_library.html#standard-library-prelude) 中，这意味着无需手动导入。

### 创建一个新的存储映射 (Creating a New Storage Map)

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

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

```
    map: StorageMap<Address, u64> = StorageMap {},

```

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

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

Storage maps, just like `Vec<T>` and `StorageVec<T>`, are implemented using generics which means that the `StorageMap<K, V>` type provided by the standard library can map keys of any type `K` to values of any type `V`. In the example above, we’ve told the Sway compiler that the `StorageMap<K, V>` in `map` will map keys of type `Address` to values of type `u64`.

存储映射，就像`Vec<T>`和`StorageVec<T>`一样，是使用泛型实现的，这意味着标准库提供的`StorageMap<K, V>`类型可以映射任何类型的键`K`到任何类型`V`的值。在上面的示例中，我们告诉 Sway 编译器，`map` 中的`StorageMap<K, V>` 会将类型为 `Address` 的键映射到类型为 `u64` 的值。

### 更新存储映射 (Updating a Storage Map)

To insert key-value pairs into a storage map, we can use the `insert` method, as shown below:

要将键值对插入存储映射，我们可以使用 `insert` 方法，如下所示：

```
    #[storage(write)]
    fn insert_into_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);
    }

```

Note two details here. First, in order to use `insert`, we need to first access the storage map using the `storage` keyword. Second, because `insert` requires _writing_ into storage, a `#[storage(write)]` annotation is required on the ABI function that calls `insert`.

这里注意两个细节。首先，为了使用 `insert`，我们需要先使用 `storage` 关键字访问存储映射。其次，因为 `insert` 需要 _写入_ 到存储中，所以调用 `insert` 的 ABI 函数需要一个 `#[storage(write)]` 注释。

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

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

### 访问存储映射中的值 (Accessing Values in a Storage Map)

We can get a value out of the storage map by providing its `key` to the `get` method, as shown below:

我们可以通过向 get 方法提供其 key 来从存储映射中获取值，如下所示：

```
    #[storage(read, write)]
    fn get_from_storage_map() {
        let addr1 = Address::from(0x0101010101010101010101010101010101010101010101010101010101010101);
        let addr2 = Address::from(0x0202020202020202020202020202020202020202020202020202020202020202);

        storage.map.insert(addr1, 42);
        storage.map.insert(addr2, 77);

        let value1 = storage.map.get(addr1).try_read().unwrap_or(0);
    }

```

Here, `value1` will have the value that's associated with the first address, and the result will be `42`. The `get` method returns an `Option<V>`; if there’s no value for that key in the storage map, `get` will return `Option::None`. This program handles the `Option` by calling `unwrap_or` to set `value1` to zero if `map` doesn't have an entry for the key.

此处，`value1` 将具有与第一个地址关联的值，结果将为 `42`。 `get` 方法返回一个 `Option<V>`；如果存储映射中该键没有值，`get` 将返回`Option::None`。该程序通过调用 `unwrap_or` 来处理 `Option`，如果 `map` 没有键的条目，则将 `value1` 设置为零。

### 具有多个键的存储映射 (Storage Maps with Multiple Keys)

Maps with multiple keys can be implemented using tuples as keys. For example:

可以使用元组作为键来实现具有多个键的映射。例如：

```
    map_two_keys: StorageMap<(b256, bool), b256> = StorageMap {},

```

### 嵌套存储映射 (Nested Storage Maps)

It is possible to nest storage maps as follows:

可以按如下方式嵌套存储映射：

```
    nested_map: StorageMap<u64, StorageMap<u64, u64>> = StorageMap {},

```

The nested map can then be accessed as follows:

然后可以按如下方式访问嵌套映射：

```
    #[storage(read, write)]
    fn access_nested_map() {
        storage.nested_map.get(0).insert(1, 42);
        storage.nested_map.get(2).insert(3, 24);

        assert(storage.nested_map.get(0).get(1).read() == 42);
        assert(storage.nested_map.get(0).get(0).try_read().is_none()); // Nothing inserted here
        assert(storage.nested_map.get(2).get(3).read() == 24);
        assert(storage.nested_map.get(2).get(2).try_read().is_none()); // Nothing inserted here
    }

```
