Enums
Just like other programming languages, enums (enumerations) are used in cairo to define variables that can only hold a set of predefined variants (= enum options), enhancing code readability and safety. They facilitate strong type checking and are ideal for organizing related options and supporting structured logic through pattern matching for example, which is also described in the next chapter.
In cairo, enum variants
can hold different data types (the unit type, structs, other enums, tuples, default core library types, arrays, dictionaries, ...), as shown in the code snippet below. Furthermore, as a quick reminder, enums are expressions, meaning they can return values.
#[derive(Drop, Serde, Copy, starknet::Store)]
struct Position {
x: u32,
y: u32,
}
#[derive(Drop, Serde, Copy, starknet::Store)]
enum UserCommand {
Login,
UpdateProfile,
Logout,
}
#[derive(Drop, Serde, Copy, starknet::Store)]
enum Action {
Quit,
Move: Position,
SendMessage: felt252,
ChangeAvatarColor: (u8, u8, u8),
ProfileState: UserCommand
}
Enums can be declared both inside and outside a contract. If declared outside, they need to be imported inside using the use
keyword, just like other imports.
-
Storing enums in contract
-
It is possible to store
enums
in the contract storage. But unlike most of the core library types which implement theStore
trait, enums are custom types and therefore do not automatically implement theStore
trait. The enum as well as all of its variants have to explicitly implement theStore
trait in order for it to be stored inside a contract storage. -
If all of its variants implement the
Store
trait, implementing theStore
trait on the enum is as simple as deriving it, using#[derive(starknet::Store)]
(as shown in example above). If not, theStore
trait has to be manually implemented -- see an example of manually implementing theStore
trait for a complex type in chapter Storing Arrays.
-
-
Enums as parameters and return values to entrypoints
- It is possible to pass
enums
to contract entrypoints as parameters, as well as return them from entrypoints. For that purpose, the enum needs to be serializable and dropable, hence the derivation of traitsSerde
andDrop
in the above code snippet.
- It is possible to pass
Here is an example of a contract illustrating the above statements :
#[starknet::interface]
trait IEnumContract<TContractState> {
fn register_action(ref self: TContractState, action: Action);
fn generate_default_actions_list(self: @TContractState) -> Array<Action>;
}
#[starknet::contract]
mod EnumContract {
use core::clone::Clone;
use super::IEnumContract;
use super::{Action, Position, UserCommand};
#[storage]
struct Storage {
most_recent_action: Action,
}
#[abi(embed_v0)]
impl IEnumContractImpl of IEnumContract<ContractState> {
fn register_action(ref self: ContractState, action: Action) {
// quick note: match takes ownership of variable (but enum Action implements Copy trait)
match action {
Action::Quit => { println!("Quit"); },
Action::Move(value) => { println!("Move with x: {} and y: {}", value.x, value.y); },
Action::SendMessage(msg) => { println!("Write with message: {}", msg); },
Action::ChangeAvatarColor((
r, g, b
)) => { println!("Change color to r: {}, g: {}, b: {}", r, g, b); },
Action::ProfileState(state) => {
let profile_state = match state {
UserCommand::Login => 1,
UserCommand::UpdateProfile => 2,
UserCommand::Logout => 3,
};
println!("profile_state: {}", profile_state);
}
};
self.most_recent_action.write(action);
}
fn generate_default_actions_list(self: @ContractState) -> Array<Action> {
let actions = array![
Action::Quit,
Action::Move(Position { x: 1, y: 2 }),
Action::SendMessage('here is my message'),
Action::ChangeAvatarColor((1, 2, 3)),
Action::ProfileState(UserCommand::Login),
];
actions
}
}
}