pub struct StateMap<K, V, S> { /* private fields */ }
Expand description

A high-level map based on the low-level key-value store, which is the interface provided by the chain.

In most situations, this collection should be preferred over BTreeMap and HashMap since it will be more efficient to lookup and update since costs to lookup and update will grow very slowly with the size of the collection. In contrast, using BTreeMap and HashMap almost always entails their serialization, which is linear in the size of the collection.

The cost of updates to the map are dependent on the length of K (in bytes) and the size of the data stored (V). Short keys are therefore ideal.

New maps can be constructed using the new_map method on the StateBuilder.

/// In an init method:
let mut map1 = state_builder.new_map();

/// In a receive method:
let mut map2 = host.state_builder().new_map();

Type parameters

The map StateMap<K, V, S> is parametrized by the type of keys K, the type of values V and the type of the low-level state S. In line with other Rust collections, e.g., BTreeMap and HashMap constructing the statemap via new_map does not require anything specific from K and V. However most operations do require that K is serializable and V can be stored and loaded in the context of the low-level state S.

This concretely means that K must implement Serialize and V has to implement Serial and DeserialWithState<S>. In practice, this means that keys must be flat, meaning that it cannot have any references to the low-level state. This is almost all types, except StateBox, StateMap and StateSet and types containing these.

However values may contain references to the low-level state, in particular maps may be nested.

Low-level state S

The type parameter S is extra compared to usual Rust collections. As mentioned above it specifies the low-level state implementation. This library provides two such implementations. The “external” one, which is the implementation supported by external host functions provided by the chain, and a test one. The latter one is useful for testing since it provides an implementation that is easier to construct, execute, and inspect during unit testing.

In user code this type parameter should generally be treated as boilerplate, and contract entrypoints should always be stated in terms of a generic type S that implements HasStateApi

Example
#[derive(Serial, DeserialWithState)]
#[concordium(state_parameter = "S")]
struct MyState<S: HasStateApi> {
    inner: StateMap<u64, u64, S>,
}
#[init(contract = "mycontract")]
fn contract_init<S: HasStateApi>(
    _ctx: &impl HasInitContext,
    state_builder: &mut StateBuilder<S>,
) -> InitResult<MyState<S>> {
    Ok(MyState {
        inner: state_builder.new_map(),
    })
}

#[receive(contract = "mycontract", name = "receive", return_value = "Option<u64>")]
fn contract_receive<S: HasStateApi>(
    _ctx: &impl HasReceiveContext,
    host: &impl HasHost<MyState<S>, StateApiType = S>, // the same low-level state must be propagated throughout
) -> ReceiveResult<Option<u64>> {
    let state = host.state();
    Ok(state.inner.get(&0).map(|v| *v))
}

Caution

StateMaps must be explicitly deleted when they are no longer needed, otherwise they will remain in the contract’s state, albeit unreachable.

struct MyState<S: HasStateApi> {
    inner: StateMap<u64, u64, S>,
}
fn incorrect_replace<S: HasStateApi>(
    state_builder: &mut StateBuilder<S>,
    state: &mut MyState<S>,
) {
    // The following is incorrect. The old value of `inner` is not properly deleted.
    // from the state.
    state.inner = state_builder.new_map(); // ⚠️
}

Instead, either the map should be cleared or explicitly deleted.

fn correct_replace<S: HasStateApi>(
    state_builder: &mut StateBuilder<S>,
    state: &mut MyState<S>,
) {
    state.inner.clear_flat();
}

Or alternatively

fn correct_replace<S: HasStateApi>(
    state_builder: &mut StateBuilder<S>,
    state: &mut MyState<S>,
) {
    let old_map = mem::replace(&mut state.inner, state_builder.new_map());
    old_map.delete()
}

Implementations

Lookup the value with the given key. Return None if there is no value with the given key.

Lookup a mutable reference to the value with the given key. Return None if there is no value with the given key.

Inserts the value with the given key. If a value already exists at the given key it is replaced and the old value is returned.

Get an entry for the given key.

Return true if the map contains no elements.

Clears the map, removing all key-value pairs. This also includes values pointed at, if V, for example, is a StateBox. If applicable use clear_flat instead.

Clears the map, removing all key-value pairs. This should be used over clear if it is applicable. It avoids recursive deletion of values since the values are required to be flat.

Unfortunately it is not possible to automatically choose between these implementations. Once Rust gets trait specialization then this might be possible.

Remove a key from the map, returning the value at the key if the key was previously in the map.

Caution: If V is a StateBox, StateMap, then it is important to call Deletable::delete on the value returned when you’re finished with it. Otherwise, it will remain in the contract state.

Remove a key from the map. This also deletes the value in the state.

Get an iterator over the key-value pairs of the map. The iterator returns values in increasing order of keys, where keys are ordered lexicographically via their serializations.

Like iter, but allows modifying the values during iteration.

Trait Implementations

Formats the value using the given formatter. Read more

Delete all items that this type owns in the state.

Attempt to read a structure from a given source and state, failing if an error occurs during deserialization or reading. Read more

Attempt to write the structure into the provided writer, failing if only part of the structure could be written. Read more

Make a clone of the type while using the cloned_state_api. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.