@hasDecl em Zig
O @hasDecl verifica em tempo de compilação se um tipo (geralmente um struct, enum ou union) possui uma declaração específica — seja uma função, constante ou tipo aninhado. Retorna true ou false, permitindo código genérico que se adapta às capacidades do tipo recebido.
Sintaxe
@hasDecl(comptime T: type, comptime name: []const u8) bool
Parâmetros
- T (
type, comptime): O tipo a ser inspecionado. - name (
[]const u8, comptime): O nome da declaração a verificar, como string.
Valor de retorno
Retorna bool — true se o tipo T possui uma declaração pública com o nome especificado, false caso contrário.
Exemplos práticos
Exemplo 1: Verificar se tipo tem método
const std = @import("std");
const Animal = struct {
nome: []const u8,
pub fn falar(self: Animal) []const u8 {
_ = self;
return "...";
}
};
const Pedra = struct {
peso: f64,
};
fn descrever(valor: anytype) void {
const T = @TypeOf(valor);
if (@hasDecl(T, "falar")) {
std.debug.print("Fala: {s}\n", .{valor.falar()});
} else {
std.debug.print("(sem fala)\n", .{});
}
}
pub fn main() void {
const gato = Animal{ .nome = "Gato" };
const pedra = Pedra{ .peso = 2.5 };
descrever(gato); // Fala: ...
descrever(pedra); // (sem fala)
}
Exemplo 2: Interface opcional com fallback
const std = @import("std");
fn formatar(valor: anytype) []const u8 {
const T = @TypeOf(valor);
if (@hasDecl(T, "formato")) {
return T.formato;
} else {
return @typeName(T);
}
}
const Celsius = struct {
pub const formato = "°C";
graus: f64,
};
const Ponto = struct {
x: f64,
y: f64,
};
pub fn main() void {
const temp = Celsius{ .graus = 25.0 };
const ponto = Ponto{ .x = 1, .y = 2 };
std.debug.print("{s}\n", .{formatar(temp)}); // °C
std.debug.print("{s}\n", .{formatar(ponto)}); // Ponto
}
Exemplo 3: Verificar presença de deinit
const std = @import("std");
fn limpar(valor: anytype) void {
const T = @TypeOf(value);
const info = @typeInfo(T);
if (info == .pointer) {
const Child = info.pointer.child;
if (@hasDecl(Child, "deinit")) {
valor.deinit();
}
}
}
Casos de uso comuns
- Interfaces opcionais: Verificar se um tipo implementa métodos específicos antes de chamá-los.
- Código genérico adaptativo: Funções que se comportam diferente dependendo das capacidades do tipo.
- Frameworks e bibliotecas: Detectar hooks e callbacks opcionais definidos pelo usuário.
- Validação em comptime: Gerar
@compileErrorse um tipo obrigatório não possuir uma declaração esperada.
Builtins relacionados
- @hasField — Verifica se um tipo possui um campo (não declaração)
- @typeInfo — Informações detalhadas sobre um tipo
- @typeName — Nome de um tipo como string
- @field — Acessa campo por nome em comptime