An enum is a named closed set of alternatives.
enum Direction {
North
East
South
West
}Enum and variant names use PascalCase.
Unit variants
A unit variant carries no payload.
direction := NorthThe expected enum type resolves an unambiguous bare variant. Qualify a variant when context is insufficient or names collide.
direction := Direction.NorthConstruction and pattern resolution use the expected enum independently. A
bare Ready can therefore be clear in an annotated binding yet ambiguous in an
unconstrained expression. Qualification is a source-level disambiguator; it
does not create a different variant.
Tuple variants
A tuple-style variant carries positional payloads.
enum Shape {
Point
Circle(float)
Rectangle(float, float)
}
shape := Circle(10.0)Destructure the payload in a pattern.
match shape {
Point => 0.0
Circle(radius) => 3.14159 * radius * radius
Rectangle(width, height) => width * height
}Struct variants
A struct-style variant names its payload fields.
enum Message {
Quit
Move { x: int, y: int }
Write { text: string }
}
message := Move { x: 10, y: 20 }Named payloads are appropriate when positions would be unclear or likely to change.
match message {
Move { x, y } => move_to(x, y)
Write { text } => println(text)
Quit => stop()
}One enum may mix unit, tuple, and struct-style variants. Each variant has its own constructor shape, and using the wrong arity or field set is a compile-time error.
Exhaustiveness
Because an enum is closed, match can prove whether all variants are handled.
fn opposite(direction: Direction): Direction {
return match direction {
North => South
East => West
South => North
West => East
}
}Adding a variant can make existing matches non-exhaustive, which turns a domain expansion into a compile-time migration task.
Integer enums
Variants may have integer discriminants.
enum HttpStatus {
Ok = 200
NotFound = 404
}Use explicit discriminants for external protocols or stable numeric representations. Do not mix data-carrying variants and integer discriminants in one enum.
Variants without an explicit discriminant use the compiler’s sequential enum rule. Do not expose an implicit number as a stable wire contract unless the ABI specification guarantees it.
An integer-valued enum remains an enum, not an int. Ordinary integer
arithmetic and construction from an unchecked integer require an explicit API;
the declaration does not make every integer a valid variant.
Generics
enum Tree[T] {
Leaf(T)
Branch([]Tree[T])
}Payloads are checked after substituting the enum’s concrete type arguments.
Expected types can infer the generic argument during construction:
tree: Tree[string] = Leaf("root")Without enough surrounding information, supply the type through an annotation or another constrained use.
Recursion
Recursive payloads need a finite representation. A managed collection can introduce the required indirection.
enum Expression {
Literal(int)
Add([]Expression)
}A variant that embeds its own full enum directly without indirection is rejected.
The same finite-layout check follows mutually recursive structs and enums. A list, another managed container, or a supported indirect representation breaks the infinite inline cycle; merely moving the direct cycle through a second named type does not.
Methods
Enums can participate in inherent and trait implementations like other named types.
impl Direction {
fn is_horizontal(self): bool {
return match self {
East | West => true
_ => false
}
}
}Use a named enum when alternatives need stable variant names, domain-specific payloads, methods, derives, or a public identity.
Payload values retain their ordinary ownership and cleanup behavior. Switching on a variant observes its tag; destructuring then binds that variant’s payload without changing the source type of the other alternatives.