@compileError em Zig — Referência e Exemplos

@compileError em Zig

O @compileError gera um erro de compilação com uma mensagem personalizada. É usado principalmente em código genérico e metaprogramação para rejeitar tipos ou configurações inválidos em tempo de compilação, antes que o programa seja executado. Esse mecanismo garante que problemas sejam detectados o mais cedo possível.

Sintaxe

@compileError(comptime msg: []const u8) noreturn

O que faz

O @compileError interrompe a compilação e exibe a mensagem fornecida como erro. O compilador aponta exatamente a linha onde o @compileError foi invocado, facilitando a identificação do problema. Esse builtin é especialmente poderoso em branches de switch ou if em comptime que representam caminhos de código inválidos.

Parâmetros

  • msg ([]const u8, comptime): A mensagem de erro a ser exibida pelo compilador. Deve ser uma string conhecida em tempo de compilação. Pode ser construída por concatenação de strings literais.

Valor de retorno

O tipo de retorno é noreturn — a compilação é interrompida e nenhum valor é produzido.

Exemplos práticos

Exemplo 1: Validação de tipos em funções genéricas

const std = @import("std");

fn somar(a: anytype, b: anytype) @TypeOf(a, b) {
    const T = @TypeOf(a, b);
    const info = @typeInfo(T);

    switch (info) {
        .int, .comptime_int => return a + b,
        .float, .comptime_float => return a + b,
        else => @compileError(
            "somar() requer tipos numéricos, recebeu: " ++ @typeName(T),
        ),
    }
}

test "somar numéricos" {
    try std.testing.expect(somar(@as(i32, 10), @as(i32, 20)) == 30);
    try std.testing.expect(somar(@as(f64, 1.5), @as(f64, 2.5)) == 4.0);

    // A linha abaixo geraria erro de compilação:
    // _ = somar("hello", "world");
    // Erro: somar() requer tipos numéricos, recebeu: *const [5:0]u8
}

Exemplo 2: Restrições de plataforma

const std = @import("std");
const builtin = @import("builtin");

fn funcaoApenasLinux() void {
    if (builtin.os.tag != .linux) {
        @compileError("Esta função só é suportada em Linux");
    }
    // Implementação específica de Linux...
}

fn funcaoApenas64Bits() void {
    if (@sizeOf(usize) != 8) {
        @compileError("Esta função requer uma arquitetura de 64 bits");
    }
    // Implementação que depende de ponteiros de 64 bits...
}

Exemplo 3: Validação de campos de struct em comptime

const std = @import("std");

fn validarModelo(comptime T: type) void {
    const info = @typeInfo(T);
    if (info != .@"struct") {
        @compileError("validarModelo requer uma struct, recebeu: " ++ @typeName(T));
    }

    const fields = info.@"struct".fields;

    // Verificar que a struct tem campo "id"
    comptime {
        var tem_id = false;
        for (fields) |f| {
            if (std.mem.eql(u8, f.name, "id")) {
                tem_id = true;
                break;
            }
        }
        if (!tem_id) {
            @compileError(@typeName(T) ++ " deve ter um campo 'id'");
        }
    }
}

const Usuario = struct {
    id: u64,
    nome: []const u8,
};

// Isso compila normalmente
comptime {
    validarModelo(Usuario);
}

// const Invalido = struct { nome: []const u8 };
// validarModelo(Invalido); // Erro: Invalido deve ter um campo 'id'

Casos de uso comuns

  1. Validação de tipos genéricos: Rejeitar tipos que não satisfazem os requisitos de uma função ou struct genérica.
  2. Restrições de plataforma: Impedir compilação em plataformas não suportadas.
  3. Funcionalidade não implementada: Marcar caminhos de código que ainda não foram implementados, diferente de @panic que ocorre em runtime.
  4. Validação de constantes: Verificar que constantes de configuração têm valores válidos.
  5. API segura: Guiar desenvolvedores com mensagens de erro claras quando usam uma API incorretamente.

Diferença entre @compileError e @panic

  • @compileError interrompe a compilação — o programa nunca chega a ser executado.
  • @panic interrompe a execução — o programa compila mas falha ao encontrar o panic em runtime.

Use @compileError quando o erro pode ser detectado estaticamente. Use @panic para erros que só podem ser detectados em tempo de execução.

Builtins relacionados

  • @compileLog — Imprime valores em tempo de compilação para depuração
  • @typeName — Útil para construir mensagens de erro descritivas
  • @typeInfo — Inspeciona tipos para validação
  • @hasField — Verifica existência de campos
  • @hasDecl — Verifica existência de declarações

Tutoriais relacionados

Continue aprendendo Zig

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