Map
A Map
is a key-value store. Unlike the raw storage backend, the keys and values of a map are
typed.
Keys
The key type has to implement the
PrimaryKey
(opens in a new tab) trait.
Most commonly, the key is simply a String
or
Addr
(opens in a new tab). Other types include
binary strings (Vec<u8>
, [u8; N]
, &[u8]
), numerical types, or even tuples, which can be used
to create composite keys.
Unlike values, keys do not need to implement anything like serde::Serialize
or
serde::Deserialize
. Key encoding is handled by the PrimaryKey
trait.
Values
The values, as usual, are serialized as JSON and must implement the
Serialize
(opens in a new tab) and
Deserialize
(opens in a new tab) traits.
Operations
Similar to an Item
, a Map
defines methods like load
, save
, and may_load
. The
difference is that Map
's methods take a key as an argument.
Most CosmWasm-enabled chains will enable iteration. If they do, it is possible to iterate over the
entries in a map using methods like
keys
(opens in a new tab) or
range
(opens in a new tab).
More information can be found in the API docs (opens in a new tab).
Usage examples
Keeping users' balances
Checking a user's balance
use cw_storage_plus::Map;
let balances: Map<String, u128> = Map::new("balances");
let alice_balance = balances
.may_load(&storage, "alice".to_string())
.unwrap()
.unwrap_or(0); // if the entry does not exist yet, assume the balance is 0
assert_eq!(alice_balance, 0);
Updating a user's balance
use cw_storage_plus::Map;
let balances: Map<String, u128> = Map::new("balances");
let mut alice_balance = balances
.may_load(&storage, "alice".to_string())
.unwrap()
.unwrap_or(0); // if the entry does not exist yet, assume the balance is 0
alice_balance += 100;
balances.save(&mut storage, "alice".to_string(), &alice_balance).unwrap();
assert_eq!(balances.load(&storage, "alice".to_string()).unwrap(), 100);
Keeping users' balances with a composite key
Basic use
use cw_storage_plus::Map;
// The first string is the user's address, the second is the denomination
let balances: Map<(String, String), u128> = Map::new("balances");
let mut alice_balance = balances
.may_load(&storage, ("alice".to_string(), "uusd".to_string()))
.unwrap()
.unwrap_or(0); // if the entry does not exist yet, assume the balance is 0
alice_balance += 100;
balances
.save(&mut storage, ("alice".to_string(), "uusd".to_string()), &alice_balance)
.unwrap();
assert_eq!(
balances
.load(&storage, ("alice".to_string(), "uusd".to_string()))
.unwrap(),
100
);
Iterating over composite keys
use cw_storage_plus::Map;
let balances: Map<(String, String), u128> = Map::new("balances");
balances.save(&mut storage, ("alice".to_string(), "uusd".to_string()), &100).unwrap();
balances.save(&mut storage, ("alice".to_string(), "osmo".to_string()), &200).unwrap();
balances.save(&mut storage, ("bob".to_string(), "uusd".to_string()), &300).unwrap();
let all_balances: Vec<_> = balances
.range(&storage, None, None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(
all_balances,
vec![
(("bob".to_string(), "uusd".to_string()), 300),
(("alice".to_string(), "osmo".to_string()), 200),
(("alice".to_string(), "uusd".to_string()), 100),
]
);
let alices_balances: Vec<_> = balances
.prefix("alice".to_string())
.range(&storage, None, None, Order::Ascending)
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(alices_balances, vec![("osmo".to_string(), 200), ("uusd".to_string(), 100)]);
As seen here, the order of keys isn't always lexicographic.
If you need to rely on iteration order in maps with composite keys, here's how things work: under the hood, every component of a composite key is length-prefixed except for the last one. If you're only iterating over the last component, you can expect things to be ordered lexicographically. For non-final components, shorter strings will always come before longer ones.
In the example, note how "bob"
(a non-last component) comes before "alice"
. Also note how once
we lock the first component to "alice"
, entries are ordered lexicographically by the second
component.