@hasDecl em Zig — Referência e Exemplos

@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 booltrue 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

  1. Interfaces opcionais: Verificar se um tipo implementa métodos específicos antes de chamá-los.
  2. Código genérico adaptativo: Funções que se comportam diferente dependendo das capacidades do tipo.
  3. Frameworks e bibliotecas: Detectar hooks e callbacks opcionais definidos pelo usuário.
  4. Validação em comptime: Gerar @compileError se 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

Tutoriais relacionados

Continue aprendendo Zig

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