Enum em Zig — O que é e Como Usar

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

  1. Legibilidade: Nomes descritivos em vez de números mágicos.
  2. Segurança de tipos: O compilador impede atribuição de valores inválidos.
  3. Switch exaustivo: O compilador garante que todas as variantes sejam tratadas.
  4. 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 Result idiomá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

RecursoZigRustCJava
Valores customizadosSim (enum(u16))SimSimSim
MétodosSimSim (via impl)NãoSim
Switch exaustivoObrigatórioObrigatório (match)NãoNão
Dados por varianteVia tagged unionVia enum diretamenteNãoLimitado
Conversão para inteiro@intFromEnumas i32Implícitaordinal()

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 else em switch sobre enum próprio: Isso garante que o compilador avise quando você adicionar uma nova variante e esquecer de tratar o caso.
  • Use @tagName para 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. Use std.meta.intToEnum que retorna um error union.
  • Nomes em snake_case: Por convenção Zig, os campos de enum são em snake_case minú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: @enumFromInt com valor inválido causa comportamento indefinido. Valide o valor antes.
  • Confundir com strings: Enums não são strings. Use @tagName para obter o nome como string.
  • Usar else desnecessariamente: Evite else em 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

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.