Enums & tagged unions
A fixed set of values, or alternatives that carry data.
On this page
An enum is a fixed set of named values. A tagged union is a value that holds one of several types.
Define an enum#
enum Status { waiting, active, done }
fn main() {
let status: Status = .waiting // `.name` uses the expected type
status = Status.active
println(status) // active
println(status == .active) // true
println(status.to(int)) // 1
}
Items are numbered from 0, and the first item is the default value. Items can be separated by commas or by spaces and new lines.
Give items a value#
Choose the item type after : and give each value in parentheses:
enum HttpStatus: u16 {
ok (200)
not_found (404)
}
enum Color: String {
red ("#ff0000")
blue ("#0000ff")
}
fn main() {
println(HttpStatus.not_found.to(int)) // 404
println(Color.red) // #ff0000
println(Color.red.name()) // red
}
An enum only compares with values of the same enum: status == 0 is a compile error; convert first with .to(int).
Names and items#
| Member | Result |
|---|---|
value.name() |
the item's name |
value.to(String), "%value" |
the name, or the text for a String enum |
Enum.items() |
every item, in declaration order |
Enum.from_name(text) |
the item with that name; throws when there is none |
enum Size { small, medium, large }
fn main() {
each Size.items() as size {
print(size.name() + " ")
}
println("") // small medium large
let parsed = Size.from_name("large") !? Size.small
println(parsed) // large
}
Add methods#
Use extend to add methods and static functions to an enum:
enum Light { red, yellow, green }
extend Light {
fn next() Light {
return match this : Light {
.red => .green
.green => .yellow
.yellow => .red
}
}
static fn start() Light { return .red }
}
fn main() {
let light = Light.start()
println(light.next()) // green
}
Match on an enum#
A match that handles every item needs no default. Several items can share a case:
enum Day { mon, tue, wed, thu, fri, sat, sun }
fn kind(day: Day) String {
return match day : String {
.sat, .sun => "weekend"
default => "weekday"
}
}
fn main() {
println(kind(.sun)) // weekend
println(kind(.wed)) // weekday
}
See Control flow for patterns, ranges and guards.
Tagged unions#
A union lists the types a value can hold. Use match with as to get the value back as its own type:
union Setting : String | int | bool | null {}
fn display(value: Setting) String {
return match value : String {
String as text => text
int as number => "number %number"
bool as enabled => enabled ? "on" : "off"
null => "unset"
}
}
fn main() {
let port: Setting = 8080
println(display(port)) // number 8080
println(display(true)) // on
println(display(null)) // unset
}
- When every type is handled, no
defaultis needed. nullhere is one of the alternatives. Write?Settingfor a union value that may be missing.- A tuple alternative is matched by its type:
(String, int) as pair.
Inline unions#
For a one-off use, write the union directly as a type:
fn describe(value: String | int) String {
return match value : String {
String as text => "text: " + text
int as number => "int: %number"
}
}
fn main() {
println(describe("hi")) // text: hi
println(describe(3)) // int: 3
}
Recursive unions#
A named union can contain itself through a collection, which is useful for tree data:
union Tree : int | Array[Tree] {}
fn sum(tree: Tree) int {
return match tree : int {
int as leaf => leaf
Array[Tree] as children => <{
let total = 0
each children as child : total += sum(child)
return total
}
}
}
fn main() {
let tree: Tree = Array[Tree]{ 1, Array[Tree]{ 2, 3 }, 4 }
println(sum(tree)) // 10
}
