Ruta graveolens  ·  notes from a language experiment  ·  cultivated since 2025

Enums

An enum is defined using the enum keyword.

enum_def = "enum" IDENT "{" [ enum_variants ] "}" ;
enum_variants = enum_variant { "," enum_variant } [ "," ] ;
enum_variant = IDENT [ "(" type { "," type } [ "," ] ")" ] ;

Enum Definition

Variant names MUST be unique within an enum.

An enum with zero variants is valid and represents an uninhabited type. A zero-variant enum can never be constructed.

Enum variants are referenced using path syntax: EnumName::VariantName. An error is raised if the enum type does not exist.

An error is raised if the variant does not exist within the enum.

enum Color {
    Red,
    Green,
    Blue,
}

fn main() -> i32 {
    let c = Color::Red;
    0
}

Match on Enums

Enum values can be matched using pattern matching in match expressions. Each arm pattern uses the same path syntax as enum variant expressions.

Match expressions on enums MUST be exhaustive: all variants MUST be covered, either explicitly or via a wildcard pattern _.

enum Color { Red, Green, Blue }

fn main() -> i32 {
    let c = Color::Green;
    match c {
        Color::Red => 1,
        Color::Green => 2,
        Color::Blue => 3,
    }
}

Enum Types

Enums can be used as function parameter types, return types, and struct field types.

enum Color { Red, Green, Blue }

fn get_value(c: Color) -> i32 {
    match c {
        Color::Red => 1,
        Color::Green => 2,
        Color::Blue => 3,
    }
}

Variant Payloads

A variant MAY carry data as a tuple variant — a parenthesized, comma-separated list of one or more payload field types written after the variant name (Circle(i32), Rect(i32, i32)). A variant without a payload list is a discriminant-only variant, as before. An enum in which at least one variant carries a payload is a sum type.

Tuple-variant payloads are a preview feature: an enum declaration containing a payload-carrying variant requires the enum_payloads preview feature to be enabled (ADR-0038). Discriminant-only enums remain available without any preview feature.

The representation of an enum is a tagged union: a discriminant that selects the active variant, together with payload storage sized to accommodate the largest variant's payload. The exact layout is implementation-defined (see spec 1.3), so niche optimizations are permitted without a change to this specification.

A tuple variant is constructed by applying the variant path to payload arguments: EnumName::Variant(arg, ...). The number of arguments MUST equal the variant's payload arity, and each argument's type MUST match the corresponding declared payload type. Using a payload-carrying variant as a bare path (EnumName::Variant, with no arguments) is an error.

Binding a variant's payload in a match arm (see spec 4.7) moves the payload out of the enum value in move mode; a moved-out payload that has a destructor runs that destructor exactly once, when its binding leaves scope.

An enum's multiplicity is the join of its variants' payload multiplicities over the lattice Copy ⊑ Affine ⊑ Linear. An enum whose every payload is Copy (including a discriminant-only enum) is itself Copy; an enum a variant of which carries an Affine (drop-carrying) payload is Affine; an enum a variant of which carries a linear payload is itself linear and MUST be consumed — letting such a value be implicitly dropped is a must-consume error, exactly as for a linear struct. Consuming the enum (for example, by a match that binds and consumes the payload) discharges the obligation.

Dropping an enum value that is not consumed drops the payload of its active variant — the one selected by the discriminant at run time — running that payload's drop glue exactly once, and nothing for a discriminant-only active variant. A payload moved out of the enum beforehand (for example, through a match binding) is not dropped again at scope exit.

enum Shape { Circle(i32), Rect(i32, i32), Empty }

fn area(s: Shape) -> i32 {
    match s {
        Shape::Circle(r) => r,
        Shape::Rect(w, h) => w + h,
        Shape::Empty => 0,
    }
}

fn main() -> i32 {
    area(Shape::Rect(3, 4))
}