Enum em Zig — O que é e Como Usar
Definição
Um enum (enumeração) em Zig é um tipo que define um conjunto fixo de valores nomeados. Cada valor é representado internamente por um inteiro, mas o compilador garante que apenas valores válidos sejam usados. Enums em Zig são mais poderosos que em muitas linguagens: suportam métodos, valores customizados e são a base das tagged unions.
Por que Enums Importam
- Legibilidade: Nomes descritivos em vez de números mágicos.
- Segurança de tipos: O compilador impede atribuição de valores inválidos.
- Switch exaustivo: O compilador garante que todas as variantes sejam tratadas.
- Métodos: Enums podem ter funções associadas, encapsulando lógica.
Exemplo Prático
Enum Básico
const std = @import("std");
const DiaDaSemana = enum {
segunda,
terca,
quarta,
quinta,
sexta,
sabado,
domingo,
};
fn ehFinalDeSemana(dia: DiaDaSemana) bool {
return switch (dia) {
.sabado, .domingo => true,
else => false,
};
}
pub fn main() void {
const hoje = DiaDaSemana.sexta;
std.debug.print("É final de semana? {}\n", .{ehFinalDeSemana(hoje)});
}
Enum com Valores Explícitos
const HttpStatus = enum(u16) {
ok = 200,
created = 201,
bad_request = 400,
not_found = 404,
internal_error = 500,
};
pub fn main() void {
const status = HttpStatus.not_found;
const codigo: u16 = @intFromEnum(status);
std.debug.print("Status: {}\n", .{codigo}); // 404
}
Enum com Métodos
const Cor = enum {
vermelho,
verde,
azul,
amarelo,
pub fn ehPrimaria(self: Cor) bool {
return switch (self) {
.vermelho, .verde, .azul => true,
.amarelo => false,
};
}
pub fn nome(self: Cor) []const u8 {
return switch (self) {
.vermelho => "Vermelho",
.verde => "Verde",
.azul => "Azul",
.amarelo => "Amarelo",
};
}
};
pub fn main() void {
const cor = Cor.azul;
std.debug.print("{s} é primária? {}\n", .{ cor.nome(), cor.ehPrimaria() });
}
Enum como Índice de Array
const Direcao = enum { norte, sul, leste, oeste };
const deslocamento_x = std.EnumArray(Direcao, i32).init(.{
.norte = 0,
.sul = 0,
.leste = 1,
.oeste = -1,
});
pub fn main() void {
const dx = deslocamento_x.get(.leste);
std.debug.print("Deslocamento X para leste: {}\n", .{dx});
}
Conversões
// Enum para inteiro
const valor: u16 = @intFromEnum(HttpStatus.ok); // 200
// Inteiro para enum
const status = @enumFromInt(HttpStatus, 404); // .not_found
// Enum para string (nome do campo)
const nome = @tagName(DiaDaSemana.segunda); // "segunda"
Casos de Uso Comuns
Enums são ideais sempre que o conjunto de estados ou opções é finito e conhecido em tempo de compilação. Exemplos práticos:
- Máquinas de estado:
EstadoConexao { conectando, conectado, desconectado, erro }— cada transição fica explícita no código. - Variantes de erro descritivas: Em conjunto com tagged unions, formam o tipo
Resultidiomático de Zig. - Flags de configuração: Em vez de booleans soltos, um enum descreve melhor a intenção —
Modo { leitura, escrita, leitura_escrita }. - Protocolo de rede: Opcodes de um protocolo binário podem ser representados com
enum(u8), mapeando diretamente para o valor do byte no cabeçalho.
Comparação com Outras Linguagens
| Recurso | Zig | Rust | C | Java |
|---|---|---|---|---|
| Valores customizados | Sim (enum(u16)) | Sim | Sim | Sim |
| Métodos | Sim | Sim (via impl) | Não | Sim |
| Switch exaustivo | Obrigatório | Obrigatório (match) | Não | Não |
| Dados por variante | Via tagged union | Via enum diretamente | Não | Limitado |
| Conversão para inteiro | @intFromEnum | as i32 | Implícita | ordinal() |
Em Rust, enums podem carregar dados diretamente em cada variante (o que Zig trata como tagged union). No Zig, enums são tipos simples de enumeração, enquanto dados por variante ficam em union(enum).
Boas Práticas
- Evite
elseem switch sobre enum próprio: Isso garante que o compilador avise quando você adicionar uma nova variante e esquecer de tratar o caso. - Use
@tagNamepara logging: Imprimir@tagName(estado)é mais legível do que imprimir o inteiro subjacente. - Valide antes de
@enumFromInt: Se o inteiro vier de entrada externa (arquivo, rede), verifique se é válido antes de converter. Usestd.meta.intToEnumque retorna um error union. - Nomes em snake_case: Por convenção Zig, os campos de enum são em
snake_caseminúsculo (.not_found,.bad_request), enquanto o tipo em si usa PascalCase (HttpStatus).
Armadilhas Comuns
- Switch não exaustivo: Sem
else, o compilador exige todas as variantes. Isso é desejável — adicionar nova variante ao enum revelará todos os switches que precisam ser atualizados. - Conversão insegura:
@enumFromIntcom valor inválido causa comportamento indefinido. Valide o valor antes. - Confundir com strings: Enums não são strings. Use
@tagNamepara obter o nome como string. - Usar
elsedesnecessariamente: Eviteelseem switches de enum quando possível. Ele esconde variantes não tratadas.
Termos Relacionados
- Tagged Union — Unions discriminadas baseadas em enum
- Struct — Tipos estruturados
- Union — Tipos union
- Comptime — Enums podem ser usados em contexto comptime