Pular para o conteúdo

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.

06-enums-unit.zolo
Playground
// 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.

07-enums-tuple.zolo
Playground
// 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.

08-enums-struct.zolo
Playground
// 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.

09-enums-generic.zolo
Playground
// 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.

16-enums-discriminants.zolo
Playground
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.

17-enums-flags.zolo
Playground
// @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.

Veja também

Buscar no Zolo

9 resultados

enespt-br