|
| 1 | +import { Callout } from "nextra/components"; |
| 2 | + |
1 | 3 | # `Map`
|
2 | 4 |
|
| 5 | +A `Map` is a key-value store. Unlike the raw storage backend, the keys and |
| 6 | +values of a map are typed. |
| 7 | + |
| 8 | +## Keys |
| 9 | + |
| 10 | +The key type has to implement the |
| 11 | +[`PrimaryKey`](https://docs.rs/cw-storage-plus/latest/cw_storage_plus/trait.PrimaryKey.html) |
| 12 | +trait. Most commonly, the key is simply a `String` or |
| 13 | +[`Addr`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Addr.html). |
| 14 | +Other types include binary strings (`Vec<u8>`, `[u8; N]`, `&[u8]`), numerical |
| 15 | +types, or even tuples, which can be used to create composite keys. |
| 16 | + |
| 17 | +## Values |
| 18 | + |
| 19 | +The values, as usual, are serialized as JSON and must implement the |
| 20 | +[`Serialize`](https://docs.serde.rs/serde/trait.Serialize.html) and |
| 21 | +[`Deserialize`](https://docs.serde.rs/serde/trait.Deserialize.html) traits. |
| 22 | + |
| 23 | +## Operations |
| 24 | + |
| 25 | +Similar to an [`Item`](./item), a `Map` defines methods like `load`, `save`, and |
| 26 | +`may_load`. The difference is that `Map`'s methods take a key as an argument. |
| 27 | + |
| 28 | +Most _CosmWasm_-enabled chains will enable iteration. If they do, it is possible |
| 29 | +to iterate over the entries in a map using methods like |
| 30 | +[`keys`](https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Map.html#method.keys) |
| 31 | +or |
| 32 | +[`range`](https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Map.html#method.range). |
| 33 | + |
3 | 34 | More information can be found in the
|
4 | 35 | [API docs](https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Map.html).
|
5 | 36 |
|
6 |
| -## Overview |
7 |
| - |
8 |
| -## Examples |
| 37 | +## Usage examples |
9 | 38 |
|
10 | 39 | ### Keeping users' balances
|
| 40 | + |
| 41 | +#### Checking a user's balance |
| 42 | + |
| 43 | +```rust template="storage" |
| 44 | +use cw_storage_plus::Map; |
| 45 | + |
| 46 | +let balances: Map<String, u128> = Map::new("balances"); |
| 47 | + |
| 48 | +let alice_balance = balances |
| 49 | + .may_load(&storage, "alice".to_string()) |
| 50 | + .unwrap() |
| 51 | + .unwrap_or(0); // if the entry does not exist yet, assume the balance is 0 |
| 52 | + |
| 53 | +assert_eq!(alice_balance, 0); |
| 54 | +``` |
| 55 | + |
| 56 | +#### Updating a user's balance |
| 57 | + |
| 58 | +```rust template="storage" |
| 59 | +use cw_storage_plus::Map; |
| 60 | + |
| 61 | +let balances: Map<String, u128> = Map::new("balances"); |
| 62 | + |
| 63 | +let mut alice_balance = balances |
| 64 | + .may_load(&storage, "alice".to_string()) |
| 65 | + .unwrap() |
| 66 | + .unwrap_or(0); // if the entry does not exist yet, assume the balance is 0 |
| 67 | + |
| 68 | +alice_balance += 100; |
| 69 | + |
| 70 | +balances.save(&mut storage, "alice".to_string(), &alice_balance).unwrap(); |
| 71 | +assert_eq!(balances.load(&storage, "alice".to_string()).unwrap(), 100); |
| 72 | +``` |
| 73 | + |
| 74 | +### Keeping users' balances with a composite key |
| 75 | + |
| 76 | +#### Basic use |
| 77 | + |
| 78 | +```rust template="storage" |
| 79 | +use cw_storage_plus::Map; |
| 80 | + |
| 81 | +// The first string is the user's address, the second is the denomination |
| 82 | +let balances: Map<(String, String), u128> = Map::new("balances"); |
| 83 | + |
| 84 | +let mut alice_balance = balances |
| 85 | + .may_load(&storage, ("alice".to_string(), "uusd".to_string())) |
| 86 | + .unwrap() |
| 87 | + .unwrap_or(0); // if the entry does not exist yet, assume the balance is 0 |
| 88 | + |
| 89 | +alice_balance += 100; |
| 90 | + |
| 91 | +balances |
| 92 | + .save(&mut storage, ("alice".to_string(), "uusd".to_string()), &alice_balance) |
| 93 | + .unwrap(); |
| 94 | + |
| 95 | +assert_eq!( |
| 96 | + balances |
| 97 | + .load(&storage, ("alice".to_string(), "uusd".to_string())) |
| 98 | + .unwrap(), |
| 99 | + 100 |
| 100 | +); |
| 101 | +``` |
| 102 | + |
| 103 | +#### Iterating over composite keys |
| 104 | + |
| 105 | +```rust template="storage" |
| 106 | +use cw_storage_plus::Map; |
| 107 | + |
| 108 | +let balances: Map<(String, String), u128> = Map::new("balances"); |
| 109 | + |
| 110 | +balances.save(&mut storage, ("alice".to_string(), "uusd".to_string()), &100).unwrap(); |
| 111 | +balances.save(&mut storage, ("alice".to_string(), "osmo".to_string()), &200).unwrap(); |
| 112 | +balances.save(&mut storage, ("bob".to_string(), "uusd".to_string()), &300).unwrap(); |
| 113 | + |
| 114 | +let all_balances: Vec<_> = balances |
| 115 | + .range(&storage, None, None, Order::Ascending) |
| 116 | + .collect::<Result<_, _>>() |
| 117 | + .unwrap(); |
| 118 | + |
| 119 | +assert_eq!( |
| 120 | + all_balances, |
| 121 | + vec![ |
| 122 | + (("bob".to_string(), "uusd".to_string()), 300), |
| 123 | + (("alice".to_string(), "osmo".to_string()), 200), |
| 124 | + (("alice".to_string(), "uusd".to_string()), 100), |
| 125 | + ] |
| 126 | +); |
| 127 | + |
| 128 | +let alices_balances: Vec<_> = balances |
| 129 | + .prefix("alice".to_string()) |
| 130 | + .range(&storage, None, None, Order::Ascending) |
| 131 | + .collect::<Result<_, _>>() |
| 132 | + .unwrap(); |
| 133 | + |
| 134 | +assert_eq!(alices_balances, vec![("osmo".to_string(), 200), ("uusd".to_string(), 100)]); |
| 135 | +``` |
| 136 | + |
| 137 | +<Callout type="warning"> |
| 138 | + |
| 139 | +As seen here, the order of keys isn't always lexicographic. If you need things |
| 140 | +ordered, the safe thing to do is to collect them in a vector and `sort` it. |
| 141 | + |
| 142 | +If you'd rather avoid that, here's how things work: under the hood, every |
| 143 | +component of a composite key is length-prefixed except for the last one. If |
| 144 | +you're only iterating over the last component, you can expect things to be |
| 145 | +ordered lexicographically. For non-final components, shorter strings will always |
| 146 | +come before longer ones. |
| 147 | + |
| 148 | +In the example, note how "bob" (a non-last component) comes before `"alice"`. |
| 149 | +Also note how once we lock the first component to `"alice"`, entries are ordered |
| 150 | +lexicographically by the second component. |
| 151 | + |
| 152 | +</Callout> |
0 commit comments