Enumerations

An enum is a user-defined data structure containing one or more variants. Each variant can optionally contain typed fields. The number, and types of these fields can differ for each variant in the enumeration. Fields in enums can store any non-reference, non-tuple type, including other structs or enums.

As a simple example, consider the following enum definition in Move:

public enum Action {
    Stop,
    Pause { duration: u32 },
    MoveTo { x: u64, y: u64 },
    Jump(u64),
}

This declares an enum Action that represents different actions that can be taken by a game -- you can Stop, Pause for a given duration, MoveTo a specific location, or Jump to a specific height.

Similar to structs, enums can have abilities that control what operations can be performed on them. It is important to note however that enums cannot have the key ability since they cannot be top-level objects.

Defining Enums

Enums must be defined in a module, an enum must contain at least one variant, and each variant of an enum can either have no fields, positional fields, or named fields. Here are some examples of each:

module a::m {
    public enum Foo has drop {
        VariantWithNoFields,
        //                 ^ note: it is fine to have a trailing comma after variant declarations
    }
    public enum Bar has copy, drop {
        VariantWithPositionalFields(u64, bool),
    }
    public enum Baz has drop {
        VariantWithNamedFields { x: u64, y: bool, z: Bar },
    }
}

Enums cannot be recursive in any of their variants, so the following definitions of an enum are not allowed because they would be recursive in at least one variant.

Incorrect:

module a::m {
    public enum Foo {
        Recursive(Foo),
        //        ^ error: recursive enum variant
    }
    public enum List {
        Nil,
        Cons { head: u64, tail: List },
        //                      ^ error: recursive enum variant
    }
    public enum BTree<T> {
        Leaf(T),
        Node { left: BTree<T>, right: BTree<T> },
        //           ^ error: recursive enum variant
    }

    // Mutually recursive enums are also not allowed
    public enum MutuallyRecursiveA {
        Base,
        Other(MutuallyRecursiveB),
        //    ^^^^^^^^^^^^^^^^^^ error: recursive enum variant
    }

    public enum MutuallyRecursiveB {
        Base,
        Other(MutuallyRecursiveA),
        //    ^^^^^^^^^^^^^^^^^^ error: recursive enum variant
    }
}

Visibility

All enums are declared as public. This means that the type of the enum can be referred to from any other module. However, the variants of the enum, the fields within each variant, and the ability to create or destroy variants of the enum are internal to the module that defines the enum.

Abilities

Just like with structs, by default an enum declaration is linear and ephemeral. To use an enum value in a non-linear or non-ephemeral way -- i.e., copied, dropped, or stored in an object -- you need to grant it additional abilities by annotating them with has <ability>:

module a::m {
    public enum Foo has copy, drop {
        VariantWithNoFields,
    }
}

The ability declaration can occur either before or after the enum's variants, however only one or the other can be used, and not both. If declared after the variants, the ability declaration must be terminated with a semicolon:

module a::m {
    public enum PreNamedAbilities has copy, drop { Variant }
    public enum PostNamedAbilities { Variant } has copy, drop;
    public enum PostNamedAbilitiesInvalid { Variant } has copy, drop
    //                                                              ^ ERROR! missing semicolon

    public enum NamedInvalidAbilities has copy { Variant } has drop;
    //                                                     ^ ERROR! duplicate ability declaration
}

For more details, see the section on annotating abilities.

Naming

Enums and variants within enums must start with a capital letter A to Z. After the first letter, enum names can contain underscores _, lowercase letters a to z, uppercase letters A to Z, or digits 0 to 9.

public enum Foo { Variant }
public enum BAR { Variant }
public enum B_a_z_4_2 { V_a_riant_0 }

This naming restriction of starting with A to Z is in place to give room for future language features.

Using Enums

Creating Enum Variants

Values of an enum type can be created (or "packed") by indicating a variant of the enum, followed by a value for each field in the variant. The variant name must always be qualified by the enum's name.

Similarly to structs, for a variant with named fields, the order of the fields does not matter but the field names need to be provided. For a variant with positional fields, the order of the fields matters and the order of the fields must match the order in the variant declaration. It must also be created using () instead of {}. If the variant has no fields, the variant name is sufficient and no () or {} needs to be used.

module a::m {
    public enum Action has drop {
        Stop,
        Pause { duration: u32 },
        MoveTo { x: u64, y: u64 },
        Jump(u64),
    }
    public enum Other has drop {
        Stop(u64),
    }

    fun example() {
        // Note: The `Stop` variant of `Action` doesn't have fields so no parentheses or curlies are needed.
        let stop = Action::Stop;
        let pause = Action::Pause { duration: 10 };
        let move_to = Action::MoveTo { x: 10, y: 20 };
        let jump = Action::Jump(10);
        // Note: The `Stop` variant of `Other` does have positional fields so we need to supply them.
        let other_stop = Other::Stop(10);
    }
}

For variants with named fields you can also use the shorthand syntax that you might be familiar with from structs to create the variant:

let duration = 10;

let pause = Action::Pause { duration: duration };
// is equivalent to
let pause = Action::Pause { duration };

Pattern Matching Enum Variants and Destructuring

Since enum values can take on different shapes, dot access to fields of variants is not allowed like it is for struct fields. Instead, to access fields within a variant -- either by value, or immutable or mutable reference -- you must use pattern matching.

You can pattern match on Move values by value, immutable reference, and mutable reference. When pattern matching by value, the value is moved into the match arm. When pattern matching by reference, the value is borrowed into the match arm (either immutably or mutably). We'll go through a brief description of pattern matching using match here, but for more information on pattern matching using match in Move see the Pattern Matching section.

A match statement is used to pattern match on a Move value and consists of a number of match arms. Each match arm consists of a pattern, an arrow =>, and an expression, followed by a comma ,. The pattern can be a struct, enum variant, binding (x, y), wildcard (_ or ..), constant (ConstValue), or literal value (true, 42, and so on). The value is matched against each pattern from the top-down, and will match the first pattern that structurally matches the value. Once the value is matched, the expression on the right hand side of the => is executed.

Additionally, match arms can have optional guards that are checked after the pattern matches but before the expression is executed. Guards are specified by the if keyword followed by an expression that must evaluate to a boolean value before the =>.

module a::m {
    public enum Action has drop {
        Stop,
        Pause { duration: u32 },
        MoveTo { x: u64, y: u64 },
        Jump(u64),
    }

    public struct GameState {
        // Fields containing a game state
        character_x: u64,
        character_y: u64,
        character_height: u64,
        // ...
    }

    fun perform_action(stat: &mut GameState, action: Action) {
        match (action) {
            // Handle the `Stop` variant
            Action::Stop => state.stop(),
            // Handle the `Pause` variant
            // If the duration is 0, do nothing
            Action::Pause { duration: 0 } => (),
            Action::Pause { duration } => state.pause(duration),
            // Handle the `MoveTo` variant
            Action::MoveTo { x, y } => state.move_to(x, y),
            // Handle the `Jump` variant
            // if the game disallows jumps then do nothing
            Action::Jump(_) if (state.jumps_not_allowed()) => (),
            // otherwise, jump to the specified height
            Action::Jump(height) => state.jump(height),
        }
    }
}

To see how to pattern match on an enum to update values within it mutably, let's take the following example of a simple enum that has two variants, each with a single field. We can then write two functions, one that only increments the value of the first variant, and another that only increments the value of the second variant:

module a::m {
    public enum SimpleEnum {
        Variant1(u64),
        Variant2(u64),
    }

    public fun incr_enum_variant1(simple_enum: &mut SimpleEnum) {
        match (simple_enum) {
            SimpleEnum::Variant1(mut value) => *value += 1,
            _ => (),
        }
    }

    public fun incr_enum_variant2(simple_enum: &mut SimpleEnum) {
        match (simple_enum) {
            SimpleEnum::Variant2(mut value) => *value += 1,
            _ => (),
        }
    }
}

Now, if we have a value of SimpleEnum we can use the functions to increment the value of this variant:

let mut x = SimpleEnum::Variant1(10);
incr_enum_variant1(&mut x);
assert!(x == SimpleEnum::Variant1(11));
// Doesn't increment since it increments a different variant
incr_enum_variant2(&mut x);
assert!(x == SimpleEnum::Variant1(11));

When pattern matching on a Move value that does not have the drop ability, the value must be consumed or destructured in each match arm. If the value is not consumed or destructured in a match arm, the compiler will raise an error. This is to ensure that all possible values are handled in the match statement.

As an example, consider the following code:

module a::m {
    public enum X { Variant { x: u64 } }

    public fun bad(x: X) {
        match (x) {
            _ => (),
           // ^ ERROR! value of type `X` is not consumed or destructured in this match arm
        }
    }
}

To properly handle this, you will need to destructure X and all its variants in the match's arm(s):

module a::m {
    public enum X { Variant { x: u64 } }

    public fun good(x: X) {
        match (x) {
            // OK! Compiles since the value is destructured
            X::Variant { x: _ } => (),
        }
    }
}

Overwriting to Enum Values

As long as the enum has the drop ability, you can overwrite the value of an enum with a new value of the same type just as you might with other values in Move.

module a::m {
    public enum X has drop {
        A(u64),
        B(u64),
    }

    public fun overwrite_enum(x: &mut X) {
        *x = X::A(10);
    }
}
let mut x = X::B(20);
overwrite_enum(&mut x);
assert!(x == X::A(10));