Cheatsheet: Enums e Unions em Zig
Referência completa para enumerações e unions em Zig, incluindo tagged unions, pattern matching e padrões de uso comuns.
Enums Básicos
// Definição simples
const Cor = enum {
vermelho,
verde,
azul,
};
const minha_cor: Cor = .verde;
// Comparação
if (minha_cor == .verde) {
std.debug.print("É verde!\n", .{});
}
Enums com Tipo Inteiro
// Enum com tipo subjacente
const HttpStatus = enum(u16) {
ok = 200,
created = 201,
bad_request = 400,
not_found = 404,
internal_error = 500,
};
// Converter enum para inteiro
const codigo: u16 = @intFromEnum(HttpStatus.not_found); // 404
// Converter inteiro para enum
const status: HttpStatus = @enumFromInt(200); // .ok
Enums com Métodos
const Direcao = enum {
norte,
sul,
leste,
oeste,
const Self = @This();
pub fn oposta(self: Self) Self {
return switch (self) {
.norte => .sul,
.sul => .norte,
.leste => .oeste,
.oeste => .leste,
};
}
pub fn toString(self: Self) []const u8 {
return switch (self) {
.norte => "Norte",
.sul => "Sul",
.leste => "Leste",
.oeste => "Oeste",
};
}
pub fn eHorizontal(self: Self) bool {
return self == .leste or self == .oeste;
}
};
// Uso
const dir = Direcao.norte;
const op = dir.oposta(); // .sul
const nome = dir.toString(); // "Norte"
_ = op;
_ = nome;
Enum com Namespace
const Comando = enum {
iniciar,
parar,
pausar,
// Constantes associadas
pub const TODOS = [_]Comando{ .iniciar, .parar, .pausar };
// Função utilitária
pub fn parse(texto: []const u8) ?Comando {
const mapa = std.StaticStringMap(Comando).initComptime(.{
.{ "iniciar", .iniciar },
.{ "parar", .parar },
.{ "pausar", .pausar },
});
return mapa.get(texto);
}
};
Iteração sobre Enums
const Naipe = enum { copas, ouros, paus, espadas };
// Iterar sobre todos os valores
for (std.enums.values(Naipe)) |naipe| {
std.debug.print("Naipe: {}\n", .{naipe});
}
// Contar valores
const total = std.enums.values(Naipe).len; // 4
// EnumSet
var set = std.EnumSet(Naipe).initEmpty();
set.insert(.copas);
set.insert(.espadas);
const tem_copas = set.contains(.copas); // true
_ = total;
_ = tem_copas;
Tagged Unions
// Tagged union — combina enum e union
const Forma = union(enum) {
circulo: f64, // raio
retangulo: struct { largura: f64, altura: f64 },
triangulo: struct { base: f64, altura: f64 },
pub fn area(self: Forma) f64 {
return switch (self) {
.circulo => |r| std.math.pi * r * r,
.retangulo => |rect| rect.largura * rect.altura,
.triangulo => |tri| tri.base * tri.altura / 2.0,
};
}
};
// Criação
const c = Forma{ .circulo = 5.0 };
const r = Forma{ .retangulo = .{ .largura = 4.0, .altura = 3.0 } };
// Uso
const area_c = c.area(); // ~78.54
const area_r = r.area(); // 12.0
_ = area_c;
_ = area_r;
Pattern Matching com Switch
const Token = union(enum) {
numero: f64,
operador: u8,
parentese_abre: void,
parentese_fecha: void,
fim: void,
};
fn processar(token: Token) void {
switch (token) {
.numero => |n| {
std.debug.print("Número: {d}\n", .{n});
},
.operador => |op| {
std.debug.print("Operador: {c}\n", .{op});
},
.parentese_abre, .parentese_fecha => {
std.debug.print("Parêntese\n", .{});
},
.fim => {
std.debug.print("Fim\n", .{});
},
}
}
// Switch exaustivo — o compilador exige que todos os casos sejam cobertos
Unions sem Tag
// Union sem tag — acesso direto (unsafe)
const Registro = union {
inteiro: i64,
float: f64,
bytes: [8]u8,
};
// Útil para reinterpretar bits
var reg = Registro{ .float = 3.14 };
// Acessar como bytes é comportamento indefinido sem tag
// Use @bitCast em vez disso para reinterpretação segura
Extern Unions
// Union compatível com C
const CUnion = extern union {
i: c_int,
f: f32,
ptr: ?*anyopaque,
};
// Layout de memória idêntico ao C
Padrões com Optionals e Errors
// Optional como tagged union implícita
// ?T é essencialmente union(enum) { value: T, null: void }
var opt: ?i32 = 42;
// Desempacotar
if (opt) |valor| {
std.debug.print("Valor: {}\n", .{valor});
}
// Error union como tagged union implícita
// E!T é essencialmente union { value: T, err: E }
const resultado: anyerror!i32 = 42;
if (resultado) |valor| {
std.debug.print("Ok: {}\n", .{valor});
} else |err| {
std.debug.print("Erro: {}\n", .{err});
}
Padrão Tipo Soma (Sum Type)
// Representar JSON
const JsonValue = union(enum) {
null: void,
boolean: bool,
integer: i64,
float: f64,
string: []const u8,
array: []JsonValue,
object: std.StringHashMap(JsonValue),
pub fn isNull(self: JsonValue) bool {
return self == .null;
}
pub fn asString(self: JsonValue) ?[]const u8 {
return switch (self) {
.string => |s| s,
else => null,
};
}
};
// Representar AST
const Expr = union(enum) {
literal: f64,
variavel: []const u8,
binario: struct {
op: u8,
esq: *const Expr,
dir: *const Expr,
},
unario: struct {
op: u8,
operando: *const Expr,
},
};
Conversões e Coerções
const Animal = union(enum) {
gato: struct { nome: []const u8 },
cachorro: struct { nome: []const u8, raca: []const u8 },
};
// Verificar variante ativa
const pet = Animal{ .gato = .{ .nome = "Mimi" } };
const eh_gato = (pet == .gato); // true
_ = eh_gato;
// Obter tag como enum
const tag = std.meta.activeTag(pet); // .gato
_ = tag;
// Enum do tagged union
const AnimalTag = std.meta.Tag(Animal);
const t: AnimalTag = .gato;
_ = t;
Tabela de Referência
| Operação | Sintaxe |
|---|---|
| Definir enum | const E = enum { a, b, c }; |
| Enum tipado | const E = enum(u8) { a = 1, b = 2 }; |
| Valor enum | .variante |
| Enum → int | @intFromEnum(e) |
| Int → enum | @enumFromInt(n) |
| Tagged union | const U = union(enum) { ... }; |
| Criar union | U{ .campo = valor } |
| Switch union | switch (u) { .a => |v| ... } |
| Verificar tag | u == .variante |
| Tag ativa | std.meta.activeTag(u) |
| Iterar enum | std.enums.values(E) |
Veja Também
- Controle de Fluxo — Switch em detalhes
- Structs — Tipos compostos
- Error Handling — Erros como unions
- Padrão State Machine — Máquinas de estado com enums
- Padrão Strategy — Strategy com tagged unions