Using collections
As mentioned in the previous chapter, the online Rust Book is a great reference for folks getting started with Rust, but there are concepts that differ when we're dealing with the blockchain. One of these differences is the use of collections.
The reference-level documentation of the Rust SDK explains this concept well:
Collections that offer an alternative to standard containers from [Rust's] std::collections::* by utilizing the underlying blockchain trie storage more efficiently.
For example, the following smart contract does not work with state efficiently, because it will load the entire HashMap at the beginning of the contract call, and will save it entirely at the end, in cases when there is state modification. This is fine for small number of elements, but very inefficient for large numbers.
In chapter 1, we set the crossword puzzle solution hash when we first deployed the contract and called the initialization method new
, passing it. This would only allow us to have only one puzzle, but let's allow for many.
At a high level, let's discuss what we'll want to add if our contract is to store multiple crossword puzzles. First, we'll have the concept of many puzzles where some of them will have different states (unfinished and finished) and we'll want to know which ones are unsolved in quick way. Another thing, which is a general rule of thumb when writing smart contracts, is to anticipate what might happen if it gets a lot of usage. What if we end up with 10,000 crossword puzzles? How might that affect how many data structures we use and which ones?
LookupMap and UnorderedSet
Let's try having two specialized NEAR collections:
- A LookupMap which will store key-value pairs. (Solution hash » Puzzle object)
- An UnorderedSet containing a set (list with no duplicates) of the solution hashes for puzzles which have not been solved yet.
As you look at the list of specialized collections in the Rust SDK, you might notice some begin with Lookup
while others have Unordered
. As is written in the reference documentation, the Lookup
is non-iterable while the Unordered
collections are iterable. This means if you will need to loop through the list of contents of this data structure, you'll likely use an iterable data structure. If you'll only ever be adding and retrieving data by the key, and the key will always be known, it's more efficient to use a non-iterable collection.
So why would we have two data structures here? Again, if we end up with a large number of puzzles, we might not be able to loop through all the puzzles, looking for ones that are unsolved. Because of the limit of gas execution per transaction, we must be conscious that there can be operations which will eventually exceed this limit. I suppose we could assume that our UnorderedSet
of unsolved puzzles wouldn't contain tens of thousands of puzzles. That's one way to avoid running into limits, but we could also learn how to utilize pagination through an iterable collection like an UnorderedSet
which we'll get to later.

Art by pierced_staggg.near
As we remember from the previous chapter, every smart contract has a primary struct containing the #[near(contract_state)]
macro.
Note in the previous chapter we named our primary struct Contract
, but in this chapter we'll call it Crossword.
The name of the struct doesn't matter and there's nothing special about naming it Contract
, though you might see that convention used in several smart contracts on NEAR. We've named it something different simply to illustrate that there's no magic behind the scenes. This does mean, however, that our impl
block will also be Crossword
.
Here's how our struct will look with the iterable and non-iterable NEAR collections:
Loading...
Above, we have the puzzles
and unsolved_puzzles
fields which are collections.
We also have an owner_id
so we can exercise a common pattern in smart contract development: implementing a rudimentary permission system which can restrict access to certain functions. We'll expand on this thought in a moment.
The snippet below shows the first method in the implementation of the Crossword
struct, where the new
function sets up these two specialized collections.
Loading...