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
.
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, 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: StateSet<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_set(),
})
}
#[receive(contract = "mycontract", name = "receive", return_value = "bool")]
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<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> {
inner: StateSet<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_set(); // ⚠️
}
Instead, either the set should be cleared or explicitly deleted.
fn correct_replace<S: HasStateApi>(
state_builder: &mut StateBuilder<S>,
state: &mut MyState<S>,
) {
state.inner.clear();
}
Or alternatively
fn correct_replace<S: HasStateApi>(
state_builder: &mut StateBuilder<S>,
state: &mut MyState<S>,
) {
let old_set = mem::replace(&mut state.inner, state_builder.new_set());
old_set.delete()
}
Implementations
sourceimpl<T, S> StateSet<T, S>where
T: Serialize,
S: HasStateApi,
impl<T, S> StateSet<T, S>where
T: Serialize,
S: HasStateApi,
sourceimpl<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.
Trait Implementations
sourceimpl<T, S> Deletable for StateSet<T, S>where
S: HasStateApi,
impl<T, S> Deletable for StateSet<T, S>where
S: HasStateApi,
sourceimpl<T, S> DeserialWithState<S> for StateSet<T, S>where
S: HasStateApi,
T: Serial + DeserialWithState<S>,
impl<T, S> DeserialWithState<S> for StateSet<T, S>where
S: HasStateApi,
T: Serial + DeserialWithState<S>,
sourcefn deserial_with_state<R: Read>(state: &S, source: &mut R) -> ParseResult<Self>
fn deserial_with_state<R: Read>(state: &S, source: &mut R) -> ParseResult<Self>
sourceimpl<T, S: HasStateApi> StateClone<S> for StateSet<T, S>
impl<T, S: HasStateApi> StateClone<S> for StateSet<T, S>
sourceunsafe fn clone_state(&self, cloned_state_api: &S) -> Self
unsafe fn clone_state(&self, cloned_state_api: &S) -> Self
cloned_state_api
. Read more