Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update storage docs for ink! v4 #137

Merged
merged 78 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
33cbdf7
WIP
xermicus Jan 23, 2023
bac0ba0
eager loading best explained in storage layout section
xermicus Jan 23, 2023
fc0a0b9
rename to storage-layout
xermicus Jan 23, 2023
e83dcc3
WIP update mappings
xermicus Jan 23, 2023
daf100a
update mappings
xermicus Jan 23, 2023
a985a0b
WIP storage layout
xermicus Jan 23, 2023
bbf7610
explain packed layout
xermicus Jan 24, 2023
ee9fa22
WIP
xermicus Jan 24, 2023
1ef577b
explain lazy
xermicus Jan 24, 2023
270f826
update example
xermicus Jan 24, 2023
07398fb
WIP custom storage types
xermicus Jan 24, 2023
16d86db
custom storage types
xermicus Jan 24, 2023
037c35b
WIP metadata storage docs
xermicus Jan 24, 2023
009a0d1
update storage layout in metadata
xermicus Jan 25, 2023
fc05550
typos
xermicus Jan 25, 2023
c873ff0
Update versioned_docs/version-4.0.0-alpha.1/datastructures/custom.md
xermicus Jan 26, 2023
d39bf54
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 26, 2023
a2d4880
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 26, 2023
c5ee9db
Update versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
xermicus Jan 27, 2023
14d4b8a
Update versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
xermicus Jan 27, 2023
d23196d
Update versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
xermicus Jan 27, 2023
26ffbb3
Update versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
xermicus Jan 27, 2023
b75865e
Update versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
xermicus Jan 27, 2023
601cb69
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
278b18d
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
37dd49a
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
855c005
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
3889490
Update versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
xermicus Jan 27, 2023
55ca0a9
Update versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
xermicus Jan 27, 2023
0d06b29
Update versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
xermicus Jan 27, 2023
3be2041
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
de68341
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
ed67c09
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
6a86095
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
3282161
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
a551e16
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 27, 2023
8dbce95
rework mapping overview
xermicus Jan 29, 2023
055a411
rework lazy overview
xermicus Jan 29, 2023
5685d84
add mapping example for local variable
xermicus Jan 29, 2023
76bc8ae
mapping storage access example
xermicus Jan 29, 2023
852607a
explain why iteration over mapping might be expensive
xermicus Jan 29, 2023
d1bf3a8
small fixes
xermicus Jan 29, 2023
9832d47
more fixes
xermicus Jan 29, 2023
54954a5
wip transparent hashing
xermicus Jan 30, 2023
e19ead9
fix layout of storage in metadata section
xermicus Jan 30, 2023
69544a3
Update versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
xermicus Jan 30, 2023
a4edead
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in…
xermicus Jan 30, 2023
125b4e0
Update versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
xermicus Jan 30, 2023
de865b3
Update versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
xermicus Jan 30, 2023
475082c
smol fix
xermicus Jan 30, 2023
0c59044
explain mapping loading behavior first
xermicus Jan 30, 2023
9448a2e
mention other datastructures under collections prelude
xermicus Jan 30, 2023
7c0826f
mention that the ink mapping can store a lot of values
xermicus Jan 30, 2023
ad310b3
mention pitfall of the contract trapping when decoding values too large
xermicus Jan 30, 2023
213762d
refer to ink::prelude::vec::Vec
xermicus Jan 30, 2023
c61c021
Update versioned_docs/version-4.0.0-alpha.1/datastructures/custom.md
xermicus Jan 31, 2023
9b07b1f
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 31, 2023
f8fca53
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 31, 2023
8f6fbe4
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in…
xermicus Jan 31, 2023
9464011
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 31, 2023
78a0e29
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in…
xermicus Jan 31, 2023
4cdf361
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 31, 2023
a5998c4
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in…
xermicus Jan 31, 2023
12f2884
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-la…
xermicus Jan 31, 2023
f6fb885
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in…
xermicus Jan 31, 2023
3447c94
Update versioned_docs/version-4.0.0-alpha.1/datastructures/storage-in…
xermicus Jan 31, 2023
f1d2eac
Merge branch 'master' into cl/storage-docs
xermicus Feb 1, 2023
53acaea
Apply suggestions from code review
xermicus Feb 1, 2023
e02fab3
impl nandos comments
xermicus Feb 1, 2023
ac6d467
impl greens comments
xermicus Feb 1, 2023
3418267
fix oopise
xermicus Feb 1, 2023
8e0891d
link ink::storage_item macro docs
xermicus Feb 1, 2023
6942184
Fix some typos
HCastano Feb 2, 2023
04fd497
Couple of small nits
HCastano Feb 2, 2023
e22dab2
Fix some typos
HCastano Feb 2, 2023
ce6bb9f
Remove trailing whitespaces for entire PR
HCastano Feb 2, 2023
0eb238a
Merge branch 'master' into cl/storage-docs
xermicus Feb 2, 2023
5481b4d
better explain storage_item macro
xermicus Feb 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
578 changes: 437 additions & 141 deletions static/img/kv.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 92 additions & 23 deletions versioned_docs/version-4.0.0-alpha.1/datastructures/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,117 @@ title: Custom Data Structures
slug: /datastructures/custom-datastructure
---

:::caution
TODO
While the `ink_storage` crate provides useful utilities and data structures to organize and
manipulate the contract's storagem, contract authors are not limited by its capabilities.
xermicus marked this conversation as resolved.
Show resolved Hide resolved

Beware, this page is no longer up to date for 4.0!
:::
## Using custom types on storage
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could merge the "Pre-ink! v4.0.0-beta storage" chapter here, to provide information about how new traits differ from the old traits?

Any custom type wanting to be compatible with ink! storage must implement the
[`Storable`](https://docs.rs/ink_storage_traits/4.0.0-beta/ink_storage_traits/trait.Storable.html)
trait, so it can be SCALE
[`encoded`](https://docs.rs/parity-scale-codec/3.2.2/parity_scale_codec/trait.Encode.html)
and
[`decoded`](https://docs.rs/parity-scale-codec/3.2.2/parity_scale_codec/trait.Decode.html).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to highlight how the developer can implement this trait.

  • Either implement it manually
  • Or derive from the Storable.
  • Or by using blanket implementation for packed types by implementing scale Encode and Decode.

Also, maybe it would be nice to add an example for each case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into that 👍

Copy link
Contributor Author

@xermicus xermicus Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this already well covered by the example Artem is working on? I'd prefer to keep that section here short, and instead link his example (and extend his example if anything is missing). WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd leave the simple derive example for now and do a follow-up with the manual implementation. This PR is already big enough as is

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I didn't mean to describe it fully here, only to mention that it has 3 variants of the implementation=)

I guess in the future we need to add something like "More detailed about it [here](And here link to Artem's&Ivan's documentation)"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a sentence to mention manual implementation

Additionaly, the traits
xermicus marked this conversation as resolved.
Show resolved Hide resolved
[`StorageLayout`](https://docs.rs/ink_storage/latest/ink_storage/traits/trait.StorageLayout.html)
and [`TypeInfo`](https://docs.rs/scale-info/2.3.1/scale_info/trait.TypeInfo.html)
are required as well. But don't worry, usually these traits can just be derived:

```rust
/// A custom type on our contract storage
xermicus marked this conversation as resolved.
Show resolved Hide resolved
#[derive(scale::Decode, scale::Encode, Debug)]
xermicus marked this conversation as resolved.
Show resolved Hide resolved
#[cfg_attr(
feature = "std",
derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout)
)]
pub struct Inner {
value: bool,
}

While the `ink_storage` crate provides tons of useful utilities and data structures to organize and manipulate the contract's storage contract authors are not limited by its capabilities. By implementing the core `SpreadLayout`/`PackedLayout` traits (and the `StorageLayout` trait for supporting the metadata generated for the `.contract` bundle) users are able to define their very own custom storage data structures with their own set of requirement and features that work along the `ink_storage` data structures as long as they fulfill the mere requirements stated by those two traits.
#[ink(storage)]
pub struct MyContractStorage {
inner: Inner,
}
```

A basic example of a custom struct is shown below:
Even better: there is a macro `#[ink::storage_item]`, which derives all necessary traits for you. If there is no need to implement any special behaviour, the above code example
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long line

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also link to the storage_item docs here

can be simplified further as follows:

``` rust
struct Inner {
value: bool
```rust
/// A custom type on our contract storage
xermicus marked this conversation as resolved.
Show resolved Hide resolved
#[ink::storage_item]
pub struct Inner {
value: bool,
}

#[ink(storage)]
pub struct MyContract {
inner: Inner
pub struct SparseArray {
xermicus marked this conversation as resolved.
Show resolved Hide resolved
inner: Inner,
}
```

Compiling the above will result in errors. While having an inner struct which holds only a boolean might not be the best idea, it serves well to illustrate how to implement the trait:
## Generic storage fields

It is possible to use generic data types in your storage, as long as any generic type
satisfies the required storage trait bounds. In fact, we already witnessed this in the
previous sections about the `Mapping`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link to those docs pls


``` rust
impl SpreadLayout for Inner {
const FOOTPRINT: u64 = 1;
Let's say you want a mapping where accessing a non-existant key should just return
xermicus marked this conversation as resolved.
Show resolved Hide resolved
it's default value, akin to how mappings work in Solidity. Additionally, you want to know
how many values there are in the mapping (its length). This could be implemented as a
thin wrapper around the ink! `Mapping` as follows:


```rust
/// Values for this map need to implement the `Default` trait.
/// Naturally, they also must be compatible with contract storage.
/// Note that the underlying `Mapping` type only supports `Packed` values.
#[ink::storage_item]
pub struct DefaultMap<K, V: Packed + Default> {
values: Mapping<K, V>,
length: u32,
}
Comment on lines +87 to +94
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this example, very nice=)


fn pull_spread(ptr: &mut KeyPtr) -> Self {
Self {
value: SpreadLayout::pull_spread(ptr),
impl<K: Encode, V: Packed + Default> DefaultMap<K, V> {
/// Accessing non-existant keys will return the default value.
xermicus marked this conversation as resolved.
Show resolved Hide resolved
pub fn get(&self, key: &K) -> V {
self.values.get(key).unwrap_or_default()
}

/// Inserting into the map increases it's length by one.
xermicus marked this conversation as resolved.
Show resolved Hide resolved
pub fn set<I, E>(&mut self, key: I, value: &E)
xermicus marked this conversation as resolved.
Show resolved Hide resolved
where
I: scale::EncodeLike<K>,
E: scale::EncodeLike<V> + Storable,
{
if self.values.insert(key, value).is_none() {
self.length += 1
}
}

fn push_spread(&self, ptr: &mut KeyPtr) {
SpreadLayout::push_spread(&self.value, ptr);
/// Removing a value from the map decreases it's length by one.
xermicus marked this conversation as resolved.
Show resolved Hide resolved
pub fn remove(&mut self, key: &K) {
if self.values.take(key).is_some() {
self.length -= 1
}
}

fn clear_spread(&self, ptr: &mut KeyPtr) {
SpreadLayout::clear_spread(&self.value, ptr);
/// Return how many values the mapping contains
pub fn len(&self) -> u32 {
self.length
}
}

/// `DefaultMap` is compatible with contract storage.
#[ink(storage)]
pub struct MyContract {
my_map: DefaultMap<BlockNumber, Balance>,
}
```

You can check what each method does in the [trait's docs](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/traits/trait.SpreadLayout.html). Check how some data structures are implemented, such as [Mapping](https://docs.rs/ink_storage/4.0.0-beta/src/ink_storage/lazy/mapping.rs.html#113).
:::caution

Generic data types may substantially increase your contracts overall code size.
xermicus marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a quick mention of why this is bad? A new dev may not make the connection between code size and cost/performance

The reason for this is [Rust's monomorphization](https://rustwasm.github.io/twiggy/concepts/generic-functions-and-monomorphization.html).

:::

129 changes: 87 additions & 42 deletions versioned_docs/version-4.0.0-alpha.1/datastructures/mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,76 +3,121 @@ title: Working with Mapping
slug: /datastructures/mapping
---

:::caution
TODO
In this section we demonstrate how to work with ink! [`Mapping`](https://docs.rs/ink_storage/4.0.0-beta.1/ink_storage/struct.Mapping.html).

Beware, this page is no longer up to date for 4.0!
:::

In this section we want to demonstrate how to work with ink! [`Mapping`](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/struct.Mapping.html).

Here is an example of a mapping from a user to a number:
Here is an example of a mapping from a user to a `Balance`:

```rust
#[ink(storage)]
#[derive(SpreadAllocate)]
pub struct MyContract {
// Store a mapping from AccountIds to a u32
map: ink_storage::Mapping<AccountId, u32>,
/// Assign a balance to every account.
map: balances: ink::storage::Mapping<AccountId, Balance>
xermicus marked this conversation as resolved.
Show resolved Hide resolved
}
```

This means that for a given key, you can store a unique instance of a value type. In this
case, each "user" gets their own number.

## Initializing a Mapping
case, each "user" gets credited their own balance.

In order to correctly initialize a `Mapping` we need two things:
1. An implementation of the [`SpreadAllocate`](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/traits/trait.SpreadAllocate.html) trait on our storage struct
2. The [`ink_lang::utils::initalize_contract`](https://docs.rs/ink_lang/4.0.0-beta/ink_lang/utils/fn.initialize_contract.html) initializer
## Example: Using a `Mapping`

Not initializing storage before you use it is a common mistake that can break your smart
contract. If you do not initialize your `Mapping`'s correctly you may end up with
different `Mapping`'s operating on the same set of storage entries 😱.
The following example contract utilizes a `Mapping` so that anyone can deposit and withdraw
balance for their own account:

```rust
#![cfg_attr(not(feature = "std"), no_std)]

#[ink::contract]
mod mycontract {
use ink_storage::traits::SpreadAllocate;
use ink::storage::Mapping;

#[ink(storage)]
#[derive(SpreadAllocate)]
pub struct MyContract {
// Store a mapping from AccountIds to a u32
map: ink_storage::Mapping<AccountId, u32>,
/// Assign a balance to every account ID
balances: Mapping<AccountId, Balance>,
}

impl MyContract {
#[ink(constructor)]
pub fn new(count: u32) -> Self {
// This call is required in order to correctly initialize the
// `Mapping`s of our contract.
ink_lang::utils::initialize_contract(|contract: &mut Self| {
let caller = Self::env().caller();
contract.map.insert(&caller, &count);
})
/// Constructor to initialize the contract with an empty mapping.
#[ink(constructor, payable)]
pub fn new() -> Self {
let balances = Mapping::default();
Self { balances }
}

#[ink(constructor)]
pub fn default() -> Self {
// Even though we're not explicitly initializing the `Mapping`,
// we still need to call this
ink_lang::utils::initialize_contract(|_| {})
/// Retreive the balance of the caller.
#[ink(message)]
pub fn get_balance(&self) -> Option<Balance> {
let caller = self.env().caller();
self.balances.get(caller)
}

// Grab the number at the caller's AccountID, if it exists
#[ink(message)]
pub fn get(&self) -> u32 {
let caller = Self::env().caller();
self.map.get(&caller).unwrap_or_default()
/// Credit more money to the contract.
#[ink(message, payable)]
pub fn transfer(&mut self) {
let caller = self.env().caller();
let balance = self.balances.get(caller).unwrap_or(0);
let endowment = self.env().transferred_value();
self.balances.insert(caller, &(balance + endowment));
}

/// Withdraw all your balance from the contract.
pub fn withdraw(&mut self) {
let caller = self.env().caller();
let balance = self.balances.get(caller).unwrap();
self.balances.remove(caller);
self.env().transfer(caller, balance).unwrap()
}
}
}

```

## Considerations when using the `Mapping` type

### Updating values
xermicus marked this conversation as resolved.
Show resolved Hide resolved

The attentive reader may have noticed that accessing mapping values via the `Mapping::get()`
method will result in an owned value (a local copy), as opposed to a direct reference
into the storage. Changes to this value won't be reflected in the contracts storage
"automatically". To avoid this common pitfall, the value must be inserted again at the same
key after it was modified. The `transfer` function from above example illustrates this:

```rust
pub fn transfer(&mut self) {
let caller = self.env().caller();
// `balance` is a local value and not a reference to the value on storage!
let balance = self.balances.get(caller).unwrap_or(0);
let endowment = self.env().transferred_value();
// The following line of code would have no effect to the balance of the
// caller stored in contract storage:
//
// balance += endowment;
//
// Instead, we use the `insert` function to write it back like so:
self.balances.insert(caller, &(balance + endowment));
}
```

### Storage loading behaviour

Each `Mapping` value lives under it's own storage key. Briefly, this means that `Mapping`s are
lazily loaded in ink!. In other words, if your message only accesses a single key of a
mapping, it will not load the whole mapping but only the value being accessed.
xermicus marked this conversation as resolved.
Show resolved Hide resolved

HCastano marked this conversation as resolved.
Show resolved Hide resolved
```rust
// This causes only a single storage access and the decoding of a single "MyValue" struct,
// no matter ho many elements there are inside the mapping.
let foo: MyValue = my_mapping.get(0)?;

for n in 0..5 {
// This causes a storage access and a decoding operation for each loop iteration.
// It is not possible to "fetch" all key/value pairs directly at once.
let bar: MyValue = my_mapping.get(n)?;
}
```

Furthermore, it follows that mapping values do not have a contiguos storage layout and it is
therefore not possible to iterate over the contents of a map.
Circumventing this restriction by storing populated keys inside an `ink_prelude::Vec` might
not always be advisable: As accessing a storage cell is relatively expensive, this might
result in very high gas costs for large mappings.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe it is better not to mention it here or create a separate section to describe how iterable storage could be implemented.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we don't have ink_prelude anymore. You should use ink::prelude::vec::Vec

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe it is better not to mention it here or create a separate section to describe how iterable storage could be implemented.

I see the point, but I don't think this is something that fits into the documentation. I just saw fit to mention that as a warning here (it might not be obvious to anyone that loading from storage causes an API call each time and this can get costly).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like it here, because it is a big topic. If you use Vec, and you have a lot of keys, it will break your contract, it will not fit into the decode buffer. We don't need to propose a solution to the iterator problem.

Also, we plan to support iterating over elements of Mapping, because of blake2_128_concat, in the future.

I would prefer to keep only the line "you can't iterate over the mapping". Or "you can't iterate over the mapping, but it is possible to implement this behaviour in another way [](link to the article/section/discussion about it)"

62 changes: 19 additions & 43 deletions versioned_docs/version-4.0.0-alpha.1/datastructures/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,22 @@ title: Overview
slug: /datastructures/overview
---

The `ink_storage` crate acts as the standard storage library for ink! smart contracts. At
the moment it only provides a single low-level primitive for interacting with storage,
the [`Mapping`](https://docs.rs/ink_storage/4.0.0-beta/ink_storage/struct.Mapping.html).

The `Mapping` is a mapping of key-value pairs directly to the contract storage. Its main advantage
is to be simple and lightweight. As such, it does not provide any high-level
functionality, such as iteration or automatic clean-up. Smart contract authors will need
to implement any high level functionality themselves.

## Eager Loading

When executing a contract, all the fields of the `#[ink(storage)]` struct will be pulled
from storage, regardless of whether or not they are used during the message execution.

Smart contract authors should be aware of this behaviour since it could potentially
affect their contract performance. For example, consider the following storage struct:

```rust
#[ink(storage)]
pub struct EagerLoading {
a: i32,
b: ink_prelude::vec::Vec<i32>,
}

impl EagerLoading {
#[ink(message)]
pub fn read_a(&self) {
let a = self.a;
}
}
```

In `EagerLoading::read_a()` we only read the `a` storage item. However, the `b` storage
item will still be loaded from storage. As a reminder, this means accessing the
underlying database and SCALE decoding the value. This can incur high costs, especially
as the number of elements in `b` grows.

:::note

Eager loading does **not** apply to `Mapping` fields, though, as key lookups in mappings
are done directly from contract storage.

:::
The `ink_storage` crate acts as the standard storage library for ink! smart contracts.
At the moment it provides two primitives for interacting with storage,
[`Mapping`](https://docs.rs/ink_storage/4.0.0-beta.1/ink_storage/struct.Mapping.html)
and [`Lazy`](https://docs.rs/ink_storage/4.0.0-beta.1/ink_storage/struct.Lazy.html).

`Mapping` is a mapping of key-value pairs directly to the contract storage. It is very
similar to traditional hash tables and comparable to the `mapping` type Solidity offers.
As a core ingredient to the ink! language, its main advantage is being simple and
lightweight: It favors being efficient in terms of gas costs and code size
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not totally true. All types from the ink::prelude are packed, which means we need fully load them into memory. The purpose of the Mapping is to allow storing a lot of values=) And because we don't load all entries of the mapping into memory, we can't provide some advanced API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I see your point. But currently, we only provide a friction of the API in comparison with the stdlib HashMap. I'll try to rephrase this sentence :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of the Mapping is to allow storing a lot of values

This is actually a very good point that was missing before. I added it as well 💯

over providing a lot of advanced high-level functionality found in other implementations
like the `HashMap` type from the Rust `std` library.
xermicus marked this conversation as resolved.
Show resolved Hide resolved
Overall, the ink! `Mapping` will be solid choice for most contracts.

`Lazy` is a wrapper type that can be used over any other storage compatible type. This allows smart contract developers fine grained manual control over the layout of the
contract storage. For example, it can be used to prevent the contract from eagerly
loading large storage fields.
xermicus marked this conversation as resolved.
Show resolved Hide resolved
Conceivably, it may be desirable to change certain aspects on how your contract deals with
it's storage variables. You can find out more about this in the section about the ink!
[Storage Layout](https://use.ink/versioned_docs/version-4.0.0-alpha.1/datastructures/storage-layout).
Loading