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
.
3 Answers
I assume that with "standard mapping" you mean the maps defined in Rust's standard library: HashMap
and BTreeMap
.
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 ink_storage::Mapping
.
The first question, what is standard Mapping
?=)
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 get
and 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:
https://github.com/paritytech/ink/blob/master/crates/storage/src/collections/hashmap/mod.rs
/// 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.
-
1FYI: This data structure was removed from the public API before the ink! 3.0 release.– HCastanoCommented Mar 24, 2022 at 0:35
-