@typeInfo em Zig
O @typeInfo é um dos builtins mais poderosos do Zig para metaprogramação. Ele permite inspecionar qualquer tipo em tempo de compilação, retornando uma union detalhada com todas as informações sobre a estrutura daquele tipo. É fundamental para escrever código genérico e bibliotecas que operam sobre tipos arbitrários.
Sintaxe
@typeInfo(comptime T: type) std.builtin.Type
O que faz
O @typeInfo recebe um tipo como parâmetro e retorna uma instância da union std.builtin.Type, que contém informações detalhadas sobre o tipo fornecido. Essa union possui variantes para cada categoria de tipo em Zig: inteiros, floats, ponteiros, arrays, structs, enums, unions, funções, entre outros.
Com essas informações, é possível escrever funções genéricas que se comportam de forma diferente dependendo das propriedades do tipo recebido, implementar serialização automática, validação de tipos e muito mais.
Parâmetros
- T (
type, comptime): O tipo a ser inspecionado. Deve ser conhecido em tempo de compilação. Pode ser qualquer tipo válido em Zig, incluindo tipos primitivos, structs, enums, unions, ponteiros, arrays e tipos de função.
Valor de retorno
Retorna uma instância de std.builtin.Type, que é uma tagged union com variantes como:
.int— Informações sobre tipos inteiros (bits, sinalização).float— Informações sobre tipos de ponto flutuante.pointer— Informações sobre ponteiros (alinhamento, sentinela, tamanho).array— Informações sobre arrays (comprimento, tipo filho).@"struct"— Informações sobre structs (campos, declarações).@"enum"— Informações sobre enums (campos, tipo de tag).@"union"— Informações sobre unions (campos, tipo de tag).@"fn"— Informações sobre funções (parâmetros, retorno)- E muitas outras variantes.
Exemplos práticos
Exemplo 1: Verificando a categoria de um tipo
const std = @import("std");
fn descreverTipo(comptime T: type) []const u8 {
const info = @typeInfo(T);
return switch (info) {
.int => "tipo inteiro",
.float => "tipo ponto flutuante",
.pointer => "tipo ponteiro",
.array => "tipo array",
.@"struct" => "tipo struct",
.@"enum" => "tipo enum",
.bool => "tipo booleano",
else => "outro tipo",
};
}
test "descrever tipos" {
try std.testing.expectEqualStrings("tipo inteiro", descreverTipo(i32));
try std.testing.expectEqualStrings("tipo ponto flutuante", descreverTipo(f64));
try std.testing.expectEqualStrings("tipo booleano", descreverTipo(bool));
}
Exemplo 2: Inspecionando campos de uma struct
const std = @import("std");
const Pessoa = struct {
nome: []const u8,
idade: u32,
ativo: bool,
};
fn listarCampos(comptime T: type) void {
const info = @typeInfo(T);
switch (info) {
.@"struct" => |s| {
inline for (s.fields) |campo| {
std.debug.print("Campo: {s}, Tipo: {s}\n", .{
campo.name,
@typeName(campo.type),
});
}
},
else => @compileError("Esperava uma struct"),
}
}
test "listar campos de Pessoa" {
listarCampos(Pessoa);
// Saída:
// Campo: nome, Tipo: []const u8
// Campo: idade, Tipo: u32
// Campo: ativo, Tipo: bool
}
Exemplo 3: Serialização genérica simples
const std = @import("std");
fn toJson(comptime T: type, valor: T, writer: anytype) !void {
const info = @typeInfo(T);
switch (info) {
.int, .comptime_int => try writer.print("{}", .{valor}),
.float, .comptime_float => try writer.print("{d}", .{valor}),
.bool => try writer.print("{}", .{valor}),
.pointer => |ptr| {
if (ptr.size == .slice and ptr.child == u8) {
try writer.print("\"{s}\"", .{valor});
}
},
.@"struct" => |s| {
try writer.writeByte('{');
inline for (s.fields, 0..) |campo, i| {
if (i > 0) try writer.writeAll(", ");
try writer.print("\"{s}\": ", .{campo.name});
try toJson(campo.type, @field(valor, campo.name), writer);
}
try writer.writeByte('}');
},
else => try writer.writeAll("null"),
}
}
Casos de uso comuns
Serialização/Deserialização genérica: Iterar sobre campos de structs para converter automaticamente de/para JSON, MessagePack ou outros formatos.
Validação de tipos genéricos: Em funções genéricas, verificar se o tipo fornecido possui as propriedades necessárias (campos específicos, métodos, etc.).
Geração automática de código: Criar implementações de funções baseadas na estrutura de tipos, como
toString(), comparação, hash, etc.Debug e logging: Imprimir informações detalhadas sobre tipos para depuração em tempo de compilação.
Frameworks e bibliotecas: Construir abstrações que funcionam com qualquer tipo, como ORMs, frameworks web e sistemas de eventos.
Builtins relacionados
- @typeName — Obtém o nome de um tipo como string
- @TypeOf — Obtém o tipo de uma expressão
- @hasField — Verifica se um tipo possui determinado campo
- @hasDecl — Verifica se um tipo possui determinada declaração
- @field — Acessa campo de struct por nome