@enumFromInt em Zig — Referência e Exemplos

@enumFromInt em Zig

O @enumFromInt cria um valor enum a partir de um valor inteiro. É a operação inversa de @intFromEnum. Este builtin é especialmente útil ao deserializar dados, processar protocolos de rede ou interagir com código C que usa constantes inteiras no lugar de enums.

Sintaxe

@enumFromInt(valor: IntType) EnumType

O tipo enum de retorno é inferido pelo contexto.

O que faz

O @enumFromInt recebe um valor inteiro e o converte para a variante correspondente de um tipo enum. Se o valor inteiro não corresponder a nenhuma variante definida no enum, o comportamento é ilegal e será detectado como erro em modo debug (safety-checked).

Parâmetros

  • valor (IntType): O valor inteiro a ser convertido para enum. O tipo deve ser compatível com o tipo de tag do enum de destino.

Valor de retorno

Retorna o valor enum correspondente ao inteiro fornecido.

Exemplos práticos

Exemplo 1: Conversão básica

const std = @import("std");

const Cor = enum { vermelho, verde, azul };

test "conversão inteiro para enum" {
    const cor: Cor = @enumFromInt(0);
    try std.testing.expect(cor == .vermelho);

    const outra: Cor = @enumFromInt(2);
    try std.testing.expect(outra == .azul);
}

Exemplo 2: Deserialização de protocolo

const std = @import("std");

const TipoMensagem = enum(u8) {
    ping = 1,
    pong = 2,
    dados = 3,
    erro = 4,
    desconexao = 5,
};

const Mensagem = struct {
    tipo: TipoMensagem,
    payload: []const u8,
};

fn parsearMensagem(buffer: []const u8) !Mensagem {
    if (buffer.len < 2) return error.MensagemMuitoCurta;

    const byte_tipo = buffer[0];
    if (byte_tipo < 1 or byte_tipo > 5) return error.TipoInvalido;

    const tipo: TipoMensagem = @enumFromInt(byte_tipo);
    const tamanho_payload = buffer[1];

    if (buffer.len < 2 + tamanho_payload) return error.PayloadIncompleto;

    return .{
        .tipo = tipo,
        .payload = buffer[2 .. 2 + tamanho_payload],
    };
}

test "parsear mensagem" {
    const buffer = [_]u8{ 1, 3, 'a', 'b', 'c' };
    const msg = try parsearMensagem(&buffer);
    try std.testing.expect(msg.tipo == .ping);
    try std.testing.expectEqualStrings("abc", msg.payload);
}

Exemplo 3: Iteração sobre todos os valores de um enum

const std = @import("std");

const DiaSemana = enum(u8) {
    segunda = 0,
    terca = 1,
    quarta = 2,
    quinta = 3,
    sexta = 4,
    sabado = 5,
    domingo = 6,
};

fn nomeDodia(dia: DiaSemana) []const u8 {
    return switch (dia) {
        .segunda => "Segunda-feira",
        .terca => "Terça-feira",
        .quarta => "Quarta-feira",
        .quinta => "Quinta-feira",
        .sexta => "Sexta-feira",
        .sabado => "Sábado",
        .domingo => "Domingo",
    };
}

test "iterar sobre dias da semana" {
    // Usar @enumFromInt para iterar por todos os valores possíveis
    var i: u8 = 0;
    while (i <= 6) : (i += 1) {
        const dia: DiaSemana = @enumFromInt(i);
        const nome = nomeDodia(dia);
        std.debug.print("Dia {}: {s}\n", .{ i, nome });
    }
}

Casos de uso comuns

  1. Deserialização: Converter bytes recebidos de rede, arquivo ou API para valores enum tipados.
  2. Interoperabilidade com C: Converter constantes inteiras retornadas por funções C para enums Zig mais seguros.
  3. Iteração: Percorrer todos os valores possíveis de um enum usando um loop numérico.
  4. Tabelas de lookup: Converter índices de array para valores enum correspondentes.
  5. Protocolos binários: Parsear cabeçalhos de protocolo que usam códigos numéricos.

Cuidados importantes

  • Se o valor inteiro não corresponder a nenhuma variante válida do enum, o comportamento é ilegal. Em modo debug, isso causa um panic. Em modo release, o comportamento pode ser indefinido.
  • Sempre valide o valor inteiro antes de chamar @enumFromInt quando os dados vêm de fontes externas não confiáveis.

Comparação com C equivalente

Em C, a conversão de inteiro para enum é implícita e completamente insegura:

typedef enum { VERMELHO = 0, VERDE = 1, AZUL = 2 } Cor;

// C aceita qualquer valor, sem verificação
Cor cor = (Cor)99; // Comportamento indefinido silencioso!

Em Zig, a conversão é explícita e verificada em modo debug:

const Cor = enum { vermelho, verde, azul };
// Isso causará panic em debug se 99 não for um valor válido:
const cor: Cor = @enumFromInt(99); // panic: invalid enum value

A abordagem do Zig força o programador a validar o dado antes da conversão, eliminando uma categoria inteira de bugs silenciosos presentes em código C.

Erros comuns

Erro 1: Não validar dados externos antes da conversão.

// ERRADO — pode causar panic se byte_tipo for inválido
const tipo: TipoMensagem = @enumFromInt(byte_tipo);

// CORRETO — validar primeiro
if (byte_tipo < 1 or byte_tipo > 5) return error.TipoInvalido;
const tipo: TipoMensagem = @enumFromInt(byte_tipo);

Erro 2: Esquecer que enums com valores personalizados não são contíguos.

const Status = enum(u16) { ok = 200, nao_encontrado = 404, erro = 500 };

// Isso vai causar panic — 201 não é um valor válido!
const s: Status = @enumFromInt(201);

Erro 3: Confundir a ordem das variantes com o valor inteiro.

Se um enum não tem valores explícitos, a atribuição é sequencial começando em zero. Mas ao adicionar ou reordenar variantes, os valores mudam — isso pode quebrar compatibilidade de protocolos binários. Prefira sempre declarar os valores explicitamente quando a ordem importa.

Perguntas Frequentes

P: É possível usar @enumFromInt em tempo de compilação (comptime)?

Sim. Se o valor inteiro for conhecido em comptime, a conversão também ocorre em comptime, e qualquer valor inválido resultará em erro de compilação em vez de panic em runtime.

const cor: Cor = comptime @enumFromInt(0); // OK — resolvido em comptime

P: Como verificar com segurança se um inteiro é um valor válido de enum antes de converter?

Use std.meta.intToEnum da biblioteca padrão, que retorna um error union em vez de causar panic:

const std = @import("std");
const cor = std.meta.intToEnum(Cor, byte_recebido) catch return error.ValorInvalido;

P: @enumFromInt funciona com enums não exaustivos (_)?

Sim. Enums não exaustivos (declarados com _ como última variante) aceitam qualquer valor inteiro do tipo de tag, então @enumFromInt sempre terá sucesso com qualquer valor dentro do intervalo do tipo inteiro.

Builtins relacionados

  • @intFromEnum — Operação inversa: obtém inteiro do enum
  • @tagName — Obtém o nome da variante como string
  • @typeInfo — Inspeciona as variantes de um enum
  • @errorName — Obtém o nome de um erro

Tutoriais relacionados

Continue aprendendo Zig

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