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}Payloads
Section titled “Payloads”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.
Methods
Section titled “Methods”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", } }}Enums do not inherit
Section titled “Enums do not inherit”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 anobject with a descriptor and an enum value is a tag, so only a class canimplement oneerror: enum 'Shade' cannot extend 'main.Base' — enums have no base typeWrite 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.
Fixed representation: enum(u8)
Section titled “Fixed representation: enum(u8)”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 4Use 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 payloadAn 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 and Result are builtin enums
Section titled “Option and Result are builtin enums”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.
A complete example
Section titled “A complete example”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.