Enums
Nesta página
Um enum define um tipo que pode ser uma entre várias variantes. O
match sobre enums é exaustivo por construção — o compilador garante que
todos os casos sejam cobertos.
Variantes unitárias
A forma mais simples: variantes sem dados, usadas para estados, modos ou flags:
Color e Status — variantes sem carga; match como tabela de despacho.
// Feature: Unit enums — variants without data
// Syntax: `enum Name { A, B, C }` — each variant is a marker.
// When to use: represent a closed set of states/options
// that carry no data — flags, modes, statuses.
// Definition.
enum Color {
Red,
Green,
Blue,
}
// Construct a variant: `Color::Variant`.
let c = Color::Red
// Match all variants.
match c {
Color::Red => print("red"),
Color::Green => print("green"),
Color::Blue => print("blue"),
}
// Function that dispatches by variant.
fn css_hex(c: Color) -> str {
return match c {
Color::Red => "#ff0000",
Color::Green => "#00ff00",
Color::Blue => "#0000ff",
}
}
print(css_hex(Color::Red)) // #ff0000
print(css_hex(Color::Green)) // #00ff00
print(css_hex(Color::Blue)) // #0000ff
// Enum as a finite "state".
enum Status {
Idle,
Running,
Done,
Failed,
}
fn describe(s: Status) -> str {
return match s {
Status::Idle => "waiting",
Status::Running => "running",
Status::Done => "completed",
Status::Failed => "failed",
}
}
let states = [Status::Idle, Status::Running, Status::Done, Status::Failed]
for s in states {
print(describe(s))
}
// expected:
// waiting
// running
// completed
// failed
Variantes com dados posicionais
Cada variante pode carregar valores, tornando o enum um tipo soma (ADT):
Shape::Circle(r), Rect(w, h) — bind dos campos no padrão; if let para um único caso.
// Feature: Enums with tuple variants — variants carry positional data
// Syntax: `enum X { Var(T1, T2, ...) }` — variants are tuple "constructors".
// When to use: union types, expressing that the value is "one of these shapes",
// extracting the data via `match`.
// Shape — each variant carries its own data.
enum Shape {
Circle(float),
// radius
Rect(float, float),
// width, height
Triangle(float, float, float),
}
// 3 sides
// Dispatch by variant and bind the fields.
fn describe(s: Shape) -> str {
return match s {
Shape::Circle(r) => "circle r={r}",
Shape::Rect(w, h) => "rect {w}x{h}",
Shape::Triangle(a, b, c) => "tri {a},{b},{c}",
}
}
print(describe(Shape::Circle(5.0)))
print(describe(Shape::Rect(4.0, 6.0)))
print(describe(Shape::Triangle(3.0, 4.0, 5.0)))
// Compute approximate area.
fn area(s: Shape) -> float {
return match s {
Shape::Circle(r) => 3.14159 * r * r,
Shape::Rect(w, h) => w * h,
Shape::Triangle(a, b, c) => {
// Approximation: semi-perimeter as a proxy
let p = a + b + c
return p / 2.0
},
}
}
print(area(Shape::Circle(2.0))) // ~12.566
print(area(Shape::Rect(3.0, 4.0))) // 12
// `if let` for the case where you already know the variant.
let c = Shape::Circle(7.0)
if let Shape::Circle(r) = c {
print("circle radius={r}") // circle radius=7
}
// Result-like enum (one-tuple).
enum Parsed {
Ok(int),
Err(str),
}
fn parse_pos(s: str) -> Parsed {
if s == "1" { return Parsed::Ok(1) }
if s == "2" { return Parsed::Ok(2) }
return Parsed::Err("not a number")
}
let r = parse_pos("1")
match r {
Parsed::Ok(n) => print("got {n}"),
Parsed::Err(msg) => print("error: {msg}"),
}
Variantes com campos nomeados
Quando uma variante tem 3+ campos ou os nomes tornam o código mais claro, use a sintaxe de struct dentro do enum:
Event::Click { x, y } — eventos de UI e comandos com campos nomeados.
// Feature: Enums with struct variants — variants with NAMED fields
// Syntax: `enum X { Var { field: T, ... } }`
// When to use: variants with 3+ fields where position is not
// obvious; events, messages, commands.
// UI events — each event has specific fields.
enum Event {
Click {
x: int,
y: int,
},
KeyPress(str),
// tuple-style
Resize {
width: int,
height: int,
},
Quit,
}
// unit-style
let ev = Event::Click { x: 100, y: 200 }
match ev {
Event::Click { x, y } => print("click ({x}, {y})"),
Event::KeyPress(key) => print("key: {key}"),
Event::Resize { width, height } => print("resize {width}x{height}"),
Event::Quit => print("quit"),
}
// Multiple events.
let events = [
Event::Click { x: 10, y: 20 },
Event::KeyPress("Enter"),
Event::Resize { width: 800, height: 600 },
Event::Quit,
]
for e in events {
match e {
Event::Click { x, y } => print("click ({x},{y})"),
Event::KeyPress(k) => print("press {k}"),
Event::Resize { width, height } => print("resize {width}x{height}"),
Event::Quit => print("quit"),
}
}
// expected:
// click (10,20)
// press Enter
// resize 800x600
// quit
// Command enum — classic example for an AST.
enum Command {
Move {
dx: int,
dy: int,
},
Set {
name: str,
value: int,
},
Print {
msg: str,
},
}
fn run(cmd: Command) {
match cmd {
Command::Move { dx, dy } => print("moved {dx},{dy}"),
Command::Set { name, value } => print("set {name} = {value}"),
Command::Print { msg } => print("[LOG] {msg}"),
}
}
run(Command::Move { dx: 3, dy: -2 })
run(Command::Set { name: "ttl", value: 30 })
run(Command::Print { msg: "ok" })
Enums genéricos
Parâmetros de tipo tornam o enum reutilizável — o padrão clássico de
Maybe<T>, Result<T, E> e Either<L, R>:
Maybe<T>, Result<T, E>, Either<L, R> — tipos algébricos parametrizados.
// Feature: Generic enums — type-parameterized variants
// Syntax: `enum X<T, U> { Var(T), Other(U) }`
// When to use: reusable algebraic types — `Option<T>`,
// `Result<T, E>`, `Either<L, R>`.
// Simple Maybe — analogous to Option/Maybe.
enum Maybe<T> {
Just(T),
Nothing,
}
fn unwrap_or_zero(m: Maybe<int>) -> int {
return match m {
Maybe::Just(v) => v,
Maybe::Nothing => 0,
}
}
print(unwrap_or_zero(Maybe::Just(42))) // 42
print(unwrap_or_zero(Maybe::Nothing)) // 0
// Result with two type parameters.
enum Result<T, E> {
Ok(T),
Err(E),
}
fn parse_int(s: str) -> Result<int, str> {
if s == "1" { return Result::Ok(1) }
if s == "42" { return Result::Ok(42) }
return Result::Err("invalid: {s}")
}
let r1 = parse_int("42")
let r2 = parse_int("xx")
match r1 {
Result::Ok(n) => print("ok = {n}"),
Result::Err(e) => print("err = {e}"),
}
match r2 {
Result::Ok(n) => print("ok = {n}"),
Result::Err(e) => print("err = {e}"),
}
// expected:
// ok = 42
// err = invalid: xx
// Either — choice between two types.
enum Either<L, R> {
Left(L),
Right(R),
}
fn show(e: Either<str, int>) -> str {
return match e {
Either::Left(s) => "left: {s}",
Either::Right(n) => "right: {n}",
}
}
print(show(Either::Left("hi"))) // left: hi
print(show(Either::Right(7))) // right: 7
Enums com representação inteira
Adicione um tipo inteiro de representação e discriminantes opcionais quando os
valores precisarem corresponder a um formato de rede ou protocolo externo.
to_int, name e from_int formam a fronteira segura de conversão.
Discriminantes u8 explícitos e implícitos, nomes e conversão verificada a
partir de um inteiro.
enum Packet: u8 {
Connect = 0,
Data = 1,
Ping = 9,
Pong,
Bye = 255,
}
print(Packet::Data.to_int())
print(Packet::Pong.to_int())
print(Packet::Bye.name())
match Packet::from_int(255) {
Option::Some(p) => print(p.name()),
Option::None => print("miss"),
}
match Packet::from_int(7) {
Option::Some(p) => print(p.name()),
Option::None => print("miss"),
}
Flags de bits
@flags transforma um enum unitário em um conjunto combinável de bits. Combine
flags com |, teste a presença com contains e use os helpers imutáveis de
inserção, remoção e alternância.
Combinação, teste, conversão, complemento, nomes e iteração de flags.
// @flags bit-flag enums (SP3)
// A @flags enum declares a combinable set: each variant gets an automatic
// power-of-two value, and a value of the type is a bitmask that can hold
// zero or more flags at the same time.
@flags
enum Perm: u8 { Read, Write, Exec } // Read=1, Write=2, Exec=4
fn main() {
// ── Combining flags with |
let rw = Perm::Read | Perm::Write
print(rw.to_int()) // 3
// ── Testing membership
print(rw.contains(Perm::Read)) // true
print(rw.contains(Perm::Exec)) // false
// ── insert / remove / toggle (all return a new value; Zolo is by-value)
let ro = rw.remove(Perm::Write)
print(ro.to_int()) // 1
let rwx = rw.insert(Perm::Exec)
print(rwx.to_int()) // 7
let xor_result = rw.toggle(Perm::Write)
print(xor_result.to_int()) // 1 (Write toggled off)
// ── to_int / from_bits / from_bits_truncate
print(Perm::all().to_int()) // 7
print(Perm::empty().is_empty()) // true
match Perm::from_bits(2) {
Option::Some(p) => print(p.name()), // Write
Option::None => print("none"),
}
match Perm::from_bits(8) { // bit 8 is not declared
Option::Some(_) => print("some"),
Option::None => print("none"), // none
}
print(Perm::from_bits_truncate(9).to_int()) // 9 & 7 == 1
// ── Complement (~) — within all(), not the raw u8 width
print((~Perm::Read).to_int()) // 6 (Write|Exec)
// ── name() — human-readable display
let p = Perm::Read | Perm::Exec
print(p.name()) // Read | Exec
print(Perm::empty().name()) // empty
// ── Equality rides structural Eq over __bits
print((Perm::Read | Perm::Write) == (Perm::Write | Perm::Read)) // true
// ── Iterating set flags with iter()
var count = 0
for f in p.iter() {
count = count + f.to_int()
}
print(count) // 1 + 4 = 5
}
Desafio
Defina um enum Tree<T> com variantes Leaf(T) e Node(T, T). Escreva
uma função sum(t: Tree<int>) -> int que retorna a soma dos valores.