Structs and enums
Overview
Structs
If you're not familiar with Rust, it may be confusing that there are no classes or inheritance like other programming languages. We'll be exploring how to use structs, which are someone similar to classes, but perhaps simpler.
Remember that there will be only one struct that gets the #[near]
macro placed on it; our primary struct or singleton if you wish. Oftentimes the primary struct will contain additional structs that may, in turn, contain more structs in a neat and orderly way. You may also have structs that are used to return data to an end user, like a frontend. We'll be covering both of these cases in this chapter.
Enums
Enums are short for enumerations, and can be particularly useful if you have entities in your smart contract that transition to different states. For example, say you have a series of blockchain games where players can join, battle, and win. There might be an enumeration for AcceptingPlayers
, GameInProgress
, and GameCompleted
. Enums are also used to define discrete types of concept, like months in a year.
For our crossword puzzle, one example of an enum is the direction of the clue: either across (A) or down (D) as illustrated below. These are the only two options.

Rust has an interesting feature where enums can contain additional data. You can see examples of that here.
Using structs
Storing contract state
We're going to introduce several structs all at once. These structs are addressing a need from the previous chapter, where the puzzle itself was hardcoded and looked like this:

In this chapter, we want the ability to add multiple, custom crossword puzzles. This means we'll be storing information about the clues in the contract state. Think of a grid where there are x and y coordinates for where a clue starts. We'll also want to specify:
- Clue number
- Whether it's across or down
- The length, or number of letters in the answer
Let's dive right in, starting with our primary struct:
#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Crossword {
puzzles: LookupMap<String, Puzzle>, // ⟵ Puzzle is a struct we're defining
unsolved_puzzles: UnorderedSet<String>,
}
For now, let's ignore the macros about the structs that begin with derive
and near
.
Look at the fields inside the Crossword
struct above, and you'll see a couple types. String
is a part of Rust's standard library, but Puzzle
is something we've created:
#[near(serializers = [borsh])]
#[derive(Debug)]
pub struct Puzzle {
status: PuzzleStatus, // ⟵ An enum we'll get to soon
/// Use the CoordinatePair assuming the origin is (0, 0) in the top left side of the puzzle.
answer: Vec<Answer>, // ⟵ Another struct we've defined
}
Let's focus on the answer
field here, which is a vector of Answer
s. (A vector is nothing fancy, just a bunch of items or a "growable array" as described in the standard Rust documentation.
#[near(serializers = [json, borsh])]
#[derive(Debug)]
pub struct Answer {
num: u8,
start: CoordinatePair, // ⟵ Another struct we've defined
direction: AnswerDirection, // ⟵ An enum we'll get to soon
length: u8,
clue: String,
}