@tagName em Zig — Referência e Exemplos

@tagName em Zig

O @tagName retorna o nome do campo ativo de uma tagged union ou o nome de uma variante de enum como string. É fundamental para serialização, logging e depuração de código que trabalha com unions e enums.

Sintaxe

@tagName(valor: anytype) [:0]const u8

O que faz

O @tagName recebe um valor de enum ou tagged union e retorna o nome do campo/variante ativo como uma string constante terminada em sentinela nulo. Para enums, retorna o nome da variante atual. Para tagged unions, retorna o nome do campo ativo.

Parâmetros

  • valor (anytype): Um valor de tipo enum ou tagged union. O tipo deve ter campos nomeados — unions sem tag não são aceitas.

Valor de retorno

Retorna [:0]const u8 — uma string terminada em zero com o nome do campo ou variante ativo.

Exemplos práticos

Exemplo 1: Nome de variantes de enum

const std = @import("std");

const Estacao = enum { primavera, verao, outono, inverno };

test "tag name de enum" {
    const estacao = Estacao.verao;
    const nome = @tagName(estacao);

    try std.testing.expectEqualStrings("verao", nome);

    // Funciona com todas as variantes
    try std.testing.expectEqualStrings("primavera", @tagName(Estacao.primavera));
    try std.testing.expectEqualStrings("inverno", @tagName(Estacao.inverno));
}

Exemplo 2: Serialização de tagged union

const std = @import("std");

const Forma = union(enum) {
    circulo: f64,       // raio
    retangulo: struct { largura: f64, altura: f64 },
    triangulo: struct { base: f64, altura: f64 },
};

fn serializarForma(forma: Forma, writer: anytype) !void {
    try writer.print("{{\"tipo\": \"{s}\"", .{@tagName(forma)});

    switch (forma) {
        .circulo => |raio| {
            try writer.print(", \"raio\": {d}", .{raio});
        },
        .retangulo => |ret| {
            try writer.print(", \"largura\": {d}, \"altura\": {d}", .{
                ret.largura,
                ret.altura,
            });
        },
        .triangulo => |tri| {
            try writer.print(", \"base\": {d}, \"altura\": {d}", .{
                tri.base,
                tri.altura,
            });
        },
    }
    try writer.writeAll("}");
}

test "serializar forma" {
    var buffer: [256]u8 = undefined;
    var stream = std.io.fixedBufferStream(&buffer);
    const writer = stream.writer();

    const forma = Forma{ .circulo = 5.0 };
    try serializarForma(forma, writer);

    const saida = stream.getWritten();
    try std.testing.expectEqualStrings("{\"tipo\": \"circulo\", \"raio\": 5}", saida);
}

Exemplo 3: Logging de eventos com tagged union

const std = @import("std");

const Evento = union(enum) {
    conexao: struct { ip: []const u8, porta: u16 },
    desconexao: struct { motivo: []const u8 },
    mensagem: struct { remetente: []const u8, conteudo: []const u8 },
    erro: struct { codigo: u32, descricao: []const u8 },
};

fn logEvento(evento: Evento) void {
    std.debug.print("[EVENTO:{s}] ", .{@tagName(evento)});

    switch (evento) {
        .conexao => |c| std.debug.print("{s}:{}\n", .{ c.ip, c.porta }),
        .desconexao => |d| std.debug.print("Motivo: {s}\n", .{d.motivo}),
        .mensagem => |m| std.debug.print("De {s}: {s}\n", .{ m.remetente, m.conteudo }),
        .erro => |e| std.debug.print("Código {}: {s}\n", .{ e.codigo, e.descricao }),
    }
}

test "log de eventos" {
    logEvento(.{ .conexao = .{ .ip = "192.168.1.1", .porta = 8080 } });
    logEvento(.{ .mensagem = .{ .remetente = "Alice", .conteudo = "Olá!" } });
    logEvento(.{ .erro = .{ .codigo = 500, .descricao = "Erro interno" } });
}

Casos de uso comuns

  1. Serialização JSON/XML: Usar o nome da tag como chave de tipo ao serializar unions para formatos textuais.
  2. Logging estruturado: Incluir o tipo do evento ou estado em mensagens de log.
  3. Depuração: Imprimir qual variante está ativa durante investigação de bugs.
  4. Métricas e telemetria: Categorizar eventos por tipo usando o nome da tag.
  5. Interface de usuário: Exibir o tipo de um valor union para o usuário em formato legível.

Comportamento em comptime

@tagName pode ser usado em tempo de compilação quando o valor é conhecido em comptime. Isso permite construir strings e tabelas de despacho baseadas em variantes de enum sem overhead em runtime:

const std = @import("std");

const Operacao = enum { soma, subtracao, multiplicacao, divisao };

// Mapa de nomes em comptime (sem alocação em runtime)
const NOMES_PT = blk: {
    var mapa: [std.meta.fields(Operacao).len][]const u8 = undefined;
    mapa[@intFromEnum(Operacao.soma)] = "Soma";
    mapa[@intFromEnum(Operacao.subtracao)] = "Subtração";
    mapa[@intFromEnum(Operacao.multiplicacao)] = "Multiplicação";
    mapa[@intFromEnum(Operacao.divisao)] = "Divisão";
    break :blk mapa;
};

fn nomePortugues(op: Operacao) []const u8 {
    return NOMES_PT[@intFromEnum(op)];
}

Internacionalização e mapeamento de nomes

@tagName retorna o nome exato do identificador em Zig (inglês ou sem acentos). Para exibição ao usuário em português, use @tagName como chave interna e mantenha um mapeamento separado:

const std = @import("std");

const StatusPedido = enum { pendente, processando, enviado, entregue, cancelado };

fn labelStatus(status: StatusPedido) []const u8 {
    return switch (status) {
        .pendente    => "Aguardando confirmação",
        .processando => "Em processamento",
        .enviado     => "A caminho",
        .entregue    => "Entregue com sucesso",
        .cancelado   => "Pedido cancelado",
    };
}

fn logStatus(status: StatusPedido) void {
    // @tagName para logs internos (inglês, sem espaços)
    std.log.info("status_changed tag={s} label={s}", .{
        @tagName(status),
        labelStatus(status),
    });
}

Desempenho

@tagName compila para uma lookup em uma tabela de strings embutida no binário. O custo em runtime é equivalente a um acesso de array indexado pelo valor inteiro do enum/tag — O(1). Não há alocação de memória, pois as strings são constantes estáticas.

Comparação com equivalente em C

Em C, obter o nome de um enum como string requer código manual:

// C: boilerplate manual e propenso a erros de sincronização
typedef enum { PENDENTE, PROCESSANDO, ENVIADO } Status;

const char* nome_status(Status s) {
    switch (s) {
        case PENDENTE:    return "PENDENTE";
        case PROCESSANDO: return "PROCESSANDO";
        case ENVIADO:     return "ENVIADO";
        default:          return "DESCONHECIDO";
    }
}

Em Zig, @tagName elimina completamente esse boilerplate — o compilador mantém os nomes automaticamente:

// Zig: sem boilerplate
const nome = @tagName(status); // sempre sincronizado com o enum

Erros comuns

1. Usar @tagName em unions sem tag:

// ERRO de compilação: @tagName requer tagged union ou enum
const UnionSemTag = union {
    a: u32,
    b: f32,
};
var u: UnionSemTag = .{ .a = 1 };
// _ = @tagName(u); // erro!

// CORRETO: usar tagged union (union(enum))
const UnionComTag = union(enum) {
    a: u32,
    b: f32,
};

2. Assumir que @tagName retorna string com encoding específico: A string retornada é ASCII (identificadores Zig são ASCII), então não há preocupação com encoding, mas não espere caracteres especiais ou acentos.

Perguntas Frequentes

P: @tagName funciona com enums que têm valores inteiros explícitos?

R: Sim. O valor inteiro não afeta o nome retornado. @tagName retorna o nome do identificador, independentemente do valor associado:

const Codigo = enum(u8) { ok = 200, nao_encontrado = 404, erro = 500 };
// @tagName(Codigo.nao_encontrado) == "nao_encontrado"

P: Posso usar @tagName com std.meta.stringToEnum para conversão bidirecional?

R: Sim! std.meta.stringToEnum(MeuEnum, nome) faz a operação inversa — converte uma string para o valor de enum correspondente. Juntos, permitem serialização/desserialização simples de enums.

P: O que acontece com variantes de enum geradas por @typeInfo e inline for?

R: @tagName funciona normalmente com qualquer valor de enum válido, independentemente de como foi obtido. Iteração sobre campos via @typeInfo e uso de @tagName é um padrão comum para serialização genérica.

Builtins relacionados

Tutoriais relacionados

Continue aprendendo Zig

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