I came across the
ink_storage::Mapping data structure. The official doc seems to explain that how to use it but not why it's needed. It seems more complicated to initialize and work with than the maps defined by
Rust's standard library.
I came across the
I assume that with "standard mapping" you mean the maps defined in Rust's standard library:
The reason for the existence of
ink_storage::Mapping is that it works entirely different: It delegates the process of mapping keys to values to the
pallet-contracts. Each item you insert into the
ink_storage::Mapping will occupy a single storage cell within the contract's storage. Accesses to the map will only pull exactly the item you are accessing from storage into contract memory.
On the other hand if you just put a
BTreeMap there it will be serialized as a whole into a single storage cell. Subsequently it will be pulled as a whole from contract storage into contract memory. Not very useful to store large sets of data. You should not do that. You would either use a standard
Vec for small sets of data that you want to iterate or use
The first question, what is standard
I will describe differences for standard hashmap from the Rust -
ink_prelude::collections::HashMap. Hope it answers your question.
ink_prelude::collections::HashMap is fully stored under one storage cell. It is fully loaded into the RAM during the execution of the contract from that storage cell. That means if
HashMap contains 1000 elements, then all 1000 elements will be loaded into the RAM. It is a lot of gas.
ink_storage::Mapping is not stored in the storage cell at all.
Mapping provides simple methods like
set a value by some key. Each value's key is prefixed by the
Mapping(it knows storage prefix), and each value is stored to its storage cell under the prefixed key. That means that element is only loaded if you called the
get method and passed a valid key.
The usability of the
Mapping is much less than
HashMap. For example, you can't iterate over elements, you can't use
entry functionality, and so on. But it takes much less gas and the size in the WASM bloat.
From the sourcecode, I found this description in:
/// A hash map operating on the contract storage. /// /// Stores a mapping between keys and values. /// /// # Note /// /// Unlike Rust's standard `HashMap` that uses the [`core::hash::Hash`] trait /// in order to hash its keys the storage hash map uses the [`scale::Encode`] /// encoding in order to hash its keys using a built-in cryptographic /// hash function provided by the chain runtime. /// /// The main difference between the lower-level `LazyHashMap` and the /// `storage::HashMap` is that the latter is aware of its associated keys and /// values and operates on those instances directly as opposed to `Option` /// instances of them. Also it provides a more high-level and user focused /// API. /// /// Users should generally prefer using this storage hash map over the low-level /// `LazyHashMap` for direct usage in their smart contracts.
The critical part is that this version uses "scale::Encode" to hash the keys.