@field em Zig — Referência e Exemplos

@field em Zig

O @field permite acessar campos de structs, unions e enums usando um nome de campo determinado em tempo de compilação (comptime). Isso é fundamental para metaprogramação, pois permite iterar sobre campos e acessá-los dinamicamente sem escrever código para cada campo individualmente.

Sintaxe

@field(objeto: anytype, comptime nome_campo: []const u8) FieldType

O que faz

O @field acessa o valor de um campo específico de uma struct, union ou enum usando uma string com o nome do campo. Embora o nome seja uma string, ele deve ser conhecido em tempo de compilação (comptime), o que permite ao compilador verificar a existência do campo e resolver seu tipo estaticamente.

Parâmetros

  • objeto (anytype): A instância da struct, union ou enum cujo campo será acessado. Pode ser um valor ou um ponteiro.
  • nome_campo ([]const u8, comptime): O nome do campo como string literal ou valor comptime. Deve corresponder a um campo existente no tipo.

Valor de retorno

Retorna o valor do campo especificado. O tipo retornado é o tipo declarado para aquele campo na definição da struct/union.

Exemplos práticos

Exemplo 1: Acesso dinâmico a campos

const std = @import("std");

const Pessoa = struct {
    nome: []const u8,
    idade: u32,
    email: []const u8,
};

test "acesso dinâmico a campos" {
    const pessoa = Pessoa{
        .nome = "Maria",
        .idade = 30,
        .email = "maria@exemplo.com",
    };

    // Acessar campos por nome
    try std.testing.expectEqualStrings("Maria", @field(pessoa, "nome"));
    try std.testing.expect(@field(pessoa, "idade") == 30);
    try std.testing.expectEqualStrings("maria@exemplo.com", @field(pessoa, "email"));
}

Exemplo 2: Serialização genérica iterando sobre campos

const std = @import("std");

fn toQueryString(comptime T: type, valor: T) ![]u8 {
    var buffer: [1024]u8 = undefined;
    var stream = std.io.fixedBufferStream(&buffer);
    const writer = stream.writer();

    const fields = @typeInfo(T).@"struct".fields;
    inline for (fields, 0..) |campo, i| {
        if (i > 0) try writer.writeByte('&');

        const campo_valor = @field(valor, campo.name);
        try writer.print("{s}=", .{campo.name});

        if (@TypeOf(campo_valor) == []const u8) {
            try writer.writeAll(campo_valor);
        } else {
            try writer.print("{}", .{campo_valor});
        }
    }

    return stream.getWritten();
}

const Filtro = struct {
    pagina: u32,
    limite: u32,
    ordem: []const u8,
};

test "query string genérica" {
    const filtro = Filtro{
        .pagina = 1,
        .limite = 20,
        .ordem = "nome",
    };

    const qs = try toQueryString(Filtro, filtro);
    // Resultado: "pagina=1&limite=20&ordem=nome"
    try std.testing.expectEqualStrings("pagina=1&limite=20&ordem=nome", qs);
}

Exemplo 3: Comparação genérica de structs

const std = @import("std");

fn structsIguais(comptime T: type, a: T, b: T) bool {
    const fields = @typeInfo(T).@"struct".fields;
    inline for (fields) |campo| {
        const val_a = @field(a, campo.name);
        const val_b = @field(b, campo.name);

        if (campo.type == []const u8) {
            if (!std.mem.eql(u8, val_a, val_b)) return false;
        } else {
            if (val_a != val_b) return false;
        }
    }
    return true;
}

const Coordenada = struct {
    x: i32,
    y: i32,
    z: i32,
};

test "comparação genérica" {
    const a = Coordenada{ .x = 1, .y = 2, .z = 3 };
    const b = Coordenada{ .x = 1, .y = 2, .z = 3 };
    const c = Coordenada{ .x = 1, .y = 2, .z = 4 };

    try std.testing.expect(structsIguais(Coordenada, a, b));
    try std.testing.expect(!structsIguais(Coordenada, a, c));
}

Casos de uso comuns

  1. Serialização/Deserialização: Iterar sobre campos de structs para converter automaticamente para JSON, CSV, query strings, etc.
  2. ORM e mapeamento de dados: Mapear colunas de banco de dados para campos de struct dinamicamente.
  3. Validação genérica: Verificar valores de todos os campos de uma struct de forma genérica.
  4. Comparação e hash: Implementar igualdade e hash para structs arbitrárias.
  5. Clonagem de structs: Copiar seletivamente campos entre structs de tipos diferentes.

Builtins relacionados

  • @hasField — Verifica se um tipo possui determinado campo antes de acessá-lo
  • @typeInfo — Lista todos os campos de um tipo
  • @hasDecl — Verifica existência de declarações (funções, constantes)
  • @typeName — Obtém nome do tipo para mensagens de erro

Tutoriais relacionados

Continue aprendendo Zig

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