Struct concordium_std::StateSet
source · pub struct StateSet<T, S> { /* private fields */ }
Expand description
A high-level set of flat values 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
BTreeSet
and HashSet
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 BTreeSet
and
HashSet
almost always entails their serialization, which is linear
in the size of the collection.
The cost of updates to the set are dependent on the serialized size of the
value T
.
New sets can be constructed using the
new_set
method on the StateBuilder
.
/// In an init method:
let mut set1 = state_builder.new_set();
/// In a receive method:
let mut set2 = host.state_builder().new_set();
§Type parameters
The set StateSet<T, S>
is parametrized by the type of values T
, and
the type of the low-level state S
. In line with other Rust collections,
e.g., BTreeSet
and HashSet
constructing the stateset via
new_set
does not require anything specific from
T
. However most operations do require that T
implements
Serialize
.
Since StateSet<T, S>
itself does not implement
Serialize
sets cannot be nested. If this is really
required then a custom solution should be devised using the operations on
S
(see HasStateApi).
§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 (StateApi
), which is the
implementation supported by external host functions provided by the chain,
and a test one. The latter one
is only useful for testing with the deprecated
test_infrastructure
module.
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 and defaults to
StateApi
, unless you intend to use the deprecated testing library.
§Example
#[derive(Serial, DeserialWithState)]
#[concordium(state_parameter = "S")]
struct MyState<S: HasStateApi = StateApi> {
inner: StateSet<u64, S>,
}
#[init(contract = "mycontract")]
fn contract_init(_ctx: &InitContext, state_builder: &mut StateBuilder) -> InitResult<MyState> {
Ok(MyState {
inner: state_builder.new_set(),
})
}
#[receive(contract = "mycontract", name = "receive", return_value = "bool")]
fn contract_receive(_ctx: &ReceiveContext, host: &Host<MyState>) -> ReceiveResult<bool> {
let state = host.state();
Ok(state.inner.contains(&0))
}
§Caution
StateSet
s 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 = StateApi> {
inner: StateSet<u64, S>,
}
fn incorrect_replace(state_builder: &mut StateBuilder, state: &mut MyState) {
// The following is incorrect. The old value of `inner` is not properly deleted.
// from the state.
state.inner = state_builder.new_set(); // ⚠️
}
Instead, either the set should be cleared or explicitly deleted.
fn correct_replace(state_builder: &mut StateBuilder, state: &mut MyState) {
state.inner.clear();
}
Or alternatively
fn correct_replace(state_builder: &mut StateBuilder, state: &mut MyState) {
let old_set = mem::replace(&mut state.inner, state_builder.new_set());
old_set.delete()
}
Implementations§
source§impl<T, S> StateSet<T, S>where
T: Serialize,
S: HasStateApi,
impl<T, S> StateSet<T, S>where
T: Serialize,
S: HasStateApi,
source§impl<T, S: HasStateApi> StateSet<T, S>
impl<T, S: HasStateApi> StateSet<T, S>
sourcepub fn iter(&self) -> StateSetIter<'_, T, S> ⓘ
pub fn iter(&self) -> StateSetIter<'_, T, S> ⓘ
Get an iterator over the elements in the StateSet
. The iterator
returns elements in increasing order, where elements are ordered
lexicographically via their serializations.