Skip to content

Enums

An enum is a type with a fixed set of named variants. Variant names are snake_case, and a variant may carry a payload. Enums are built for match.

enum Status {
active
suspended
closed
}

A variant can carry named fields:

enum Payment {
cash
card(number: string)
transfer(iban: string, amount: decimal)
}
fn describe(p: Payment) -> string {
return match p {
cash => "cash",
card(n) => "card ending {n.last(4)}",
transfer(iban, amt) => "sent {amt} to {iban}",
}
}

Construct a variant with the enum name in front: Payment.card("4242"), or Payment.cash for a variant with no payload. Matching, by contrast, uses the bare variant name. It binds the payload fields positionally (card(n)), and the matched value pins their types, so you do not restate them.

An enum can carry methods, with an implicit self, and static methods called on the enum name:

enum Level {
low
high
static fn default_level() -> Level {
return Level.low
}
fn label() -> string {
return match self {
low => "low",
high => "high",
}
}
}

An enum cannot implement an interface, and it cannot extend a class. Only a class does either: an interface call finds the method through a descriptor in the object’s header, and no enum carries one — an enum(u8) has no header at all. The checker refuses both at the declaration:

interface Shows { fn show_it() -> string }
class Base { fn init() {} }
enum Colour implements Shows { red, green }
enum Shade extends Base { light, dark }
error: enum 'Colour' cannot implement 'main.Shows' — an interface value is an
object with a descriptor and an enum value is a tag, so only a class can
implement one
error: enum 'Shade' cannot extend 'main.Base' — enums have no base type

Write the behaviour as a method instead — Level.label above is the whole pattern. When you need one name to cover shapes that differ in their fields, that is what a class and an interface are for.

An enum still satisfies the Clone, Eq and Hash bounds and works as a Map key without naming any of them. It does not satisfy Order, so sort, max and min do not reach it.

A payload-free enum can commit to a one-byte layout on the declaration:

enum(u8) Display {
flex
grid
hidden
}

The value is then the bare u8 tag, variants numbered in declaration order — the same numbers an ordinary enum already uses, so nothing about behaviour changes. Matching, equality, printing, methods, map keys, and reflection all work exactly as they do without the marker.

What changes is layout. size_of answers 1, align_of answers 1, a struct holding one keeps a fixed inline layout with no pointer bits and no reference counting, and a [Display; N] array stores one byte per element:

import std.io
enum(u8) Display {
flex
grid
hidden
}
fn main() {
io.println("{size_of(Display)} {align_of(Display)} {size_of([Display; 4])}")
}
1 1 4

Use this form when you need a fixed one-byte representation, such as a compact record or a large array. Use an ordinary enum otherwise.

The checker refuses the marker, naming the rule, on:

  • an enum with any payload variant,
  • a generic enum,
  • more than 256 variants,
  • any representation other than u8.
enum(u8) Payment {
cash
card(number: string)
}
error: enum(u8) needs every variant payload-free — variant 'card' carries a payload

An enum(u8) is still a distinct type, not an integer: there is no implicit conversion in either direction. It is also still not a C ABI type, so an extern "C" record field or typed JSON refuses it exactly as an ordinary enum does. See Compile-time layout.

Option<T> and Result<T, E> are ordinary builtin enums:

enum Option<T> {
some(value: T)
none
}
enum Result<T, E> {
ok(value: T)
err(error: E)
}

That is why some, none, ok, and err are lowercase: they are variant values, and variants are snake_case. See Option and Result.

An enum with payloads and a method, matched to compute a value:

import std.io
enum Shape {
circle(r: f64)
rect(w: f64, h: f64)
fn area() -> f64 {
return match self {
circle(r) => 3.14159 * r * r,
rect(w, h) => w * h,
}
}
}
fn main() {
let s: Shape = Shape.rect(3.0, 4.0)
io.println("{s.area()}")
}

A match on an enum must cover every variant, or handle the rest with _. See Pattern matching.