The set strategy

The set strategy can be applied to fields of type Set<E> for any types E.

fields {
    #[sharding(set)]
    pub field: Set<E>,
}

Tokens. VerusSync creates a fresh token type, tok, named State::field where State is the name of the VerusSync system and field is the name of the field.

The token type tok implements the UniqueElementToken<V> trait.

For dealing with collections of tokens, VerusSync uses SetToken<K, V, tok>. It also creates a type alias for this type by appending _set to the field name, e.g., State::field_set.

Relationship between global field value and the token. Every token represents a single element of the set (given by the .element() function on the UniqueElementToken<V> trait). The value of the field on the global state is the set given by the collection of all such elements.

Manipulation of the field

Quick Reference

In the following table, e: E and s: Set<E>.

Command Meaning in transition Exchange Fn Parameter
init field = s; init field = s; Output SetToken<K, V, tok>
remove field -= set { e }; require field.contains(e);
update field = field.remove(e);
Input tok
have field >= set { e }; require field.contains(e); Input &tok
add field += set { e }; assert !field.contains(e);
update field = field.insert(e);
Output tok
remove field -= (s); require s.subset_of(field);
update field = field.difference(s);
Input SetToken<K, V, tok>
have field >= (s); require s.subset_of(field); Input &SetToken<K, V, tok>
add field += (s); assert field.disjoint(s);
update field = field.union(s);
Output SetToken<K, V, tok>

Initializing the field

Initializing the field is done with the usual init statement (as it for all strategies).

init field = s;

The instance-init function will return a token token of type SetToken<K, V, tok>, where token.set() == s.

Adding a token

To write an operation that creates a token with element e, equivalently, inserting the element e into the set, write, inside any transition! operation:

add field += set { e };

This operation has an inherent safety condition that e is not in the pre-state value of the set. The resulting token exchange function will return a token of type tok and with element() equal to e.

If you require manual proof to prove the inherent safety condition, you can add an optional by clause:

add field += set { e };
by {
    // proof goes here
};

Removing a token

To write an operation that removes a token with element e, equivalently, removing the element e from the set, write, inside any transition! operation:

remove field -= set { e };

The resulting exchange function will consume a tok token with element() equal to e.

Checking the value of the token

To check the element of the token without removing it, write, inside any transition!, readonly! or property! operation:

have field >= set { e };

The resulting exchange function will accept an immutable reference &tok (that is, it takes the token as input but does not consume it).

Operations that manipulate collections of tokens

You can also write versions of the above operations that operate on variable-sized collections of tokens rather than singleton elements. Such token collections are managed as SetToken<K, V, tok> objects.

The operations below are equivalent to the above versions whenever s == set![e], and they are all no-ops when s == set![].

To create a SetToken<K, V, tok> where tok.set() == s:

add field += (s);

To consume a SetToken<K, V, tok> where tok.set() == s:

remove field -= (s);

To check the value of a &SetToken<K, V, tok> where tok.set() == s:

have field >= (s);

Example

TODO