@compileLog em Zig — Referência e Exemplos

@compileLog em Zig

O @compileLog imprime valores em tempo de compilação, permitindo depurar código comptime. Quando presente no código, o compilador exibe os valores e gera um erro de compilação (para evitar que mensagens de debug sejam deixadas acidentalmente no código de produção).

Sintaxe

@compileLog(args: ...) void

O que faz

O @compileLog aceita qualquer número de argumentos conhecidos em tempo de compilação e os imprime durante a compilação. Os valores são exibidos com informação de tipo. Após imprimir, a compilação falha com um erro indicando que @compileLog foi encontrado — isso é intencional para garantir que chamadas de depuração não permaneçam em código final.

Parâmetros

  • args (variádico, comptime): Qualquer número de valores conhecidos em tempo de compilação. Podem ser inteiros, strings, tipos, slices e qualquer outro valor comptime.

Valor de retorno

Retorna void, mas a compilação sempre falha quando @compileLog está presente no código.

Exemplos práticos

Exemplo 1: Depurando valores comptime

const std = @import("std");

fn calcularTamanho(comptime T: type) comptime_int {
    const tamanho = @sizeOf(T);
    const alinhamento = @alignOf(T);

    // Depurar valores durante a compilação
    @compileLog("Tipo:", @typeName(T));
    @compileLog("Tamanho:", tamanho, "Alinhamento:", alinhamento);

    return tamanho;
}

// Ao compilar, o compilador mostra:
// | @compileLog output |
// | Tipo: i32          |
// | Tamanho: 4 Alinhamento: 4 |
// error: compile log statement found

Exemplo 2: Inspecionando campos de struct em comptime

const std = @import("std");

fn depurarStruct(comptime T: type) void {
    const info = @typeInfo(T);
    switch (info) {
        .@"struct" => |s| {
            @compileLog("Struct:", @typeName(T));
            @compileLog("Número de campos:", s.fields.len);

            inline for (s.fields) |campo| {
                @compileLog(
                    "  Campo:",
                    campo.name,
                    "Tipo:",
                    @typeName(campo.type),
                );
            }
        },
        else => @compileLog("Não é struct:", @typeName(T)),
    }
}

const Ponto = struct {
    x: f32,
    y: f32,
    z: f32,
};

comptime {
    depurarStruct(Ponto);
}
// Saída do compilador:
// | Struct: Ponto                     |
// | Número de campos: 3              |
// |   Campo: x Tipo: f32             |
// |   Campo: y Tipo: f32             |
// |   Campo: z Tipo: f32             |

Exemplo 3: Rastreando execução de código comptime

const std = @import("std");

fn gerarLookupTable(comptime tamanho: usize) [tamanho]u32 {
    @compileLog("Gerando tabela de tamanho:", tamanho);

    var tabela: [tamanho]u32 = undefined;
    for (0..tamanho) |i| {
        tabela[i] = @as(u32, @intCast(i)) * @as(u32, @intCast(i));

        // Depurar valores específicos (cuidado: pode gerar muita saída)
        if (i < 5) {
            @compileLog("  tabela[", i, "] =", tabela[i]);
        }
    }

    @compileLog("Tabela gerada com sucesso");
    return tabela;
}

// const tabela = comptime gerarLookupTable(100);
// Descomentar para ver a saída de depuração

Casos de uso comuns

  1. Depurar metaprogramação: Verificar valores intermediários em funções comptime que geram código ou tabelas.
  2. Entender resolução de tipos: Inspecionar como o compilador resolve tipos em funções genéricas.
  3. Verificar layout de structs: Examinar campos, tamanhos e alinhamentos durante a compilação.
  4. Rastrear execução comptime: Entender a ordem de execução e os valores produzidos por loops e condicionais em comptime.
  5. Diagnóstico de erros: Quando um @compileError é inesperado, usar @compileLog antes dele para entender o estado.

Observações importantes

  • O @compileLog sempre causa falha de compilação. Não se esqueça de removê-lo antes de finalizar o código.
  • Ele não afeta a geração de código — é puramente para depuração.
  • Diferente de std.debug.print, que funciona em runtime, @compileLog opera exclusivamente em tempo de compilação.
  • O formato da saída pode variar entre versões do compilador.

Fluxo de trabalho com @compileLog

O uso típico de @compileLog é:

  1. Adicionar @compileLog(...) para inspecionar um valor suspeito
  2. Compilar — o compilador mostra o valor e falha
  3. Analisar a saída e entender o problema
  4. Remover o @compileLog antes de commitar

Como a compilação sempre falha com @compileLog presente, é impossível deixar acidentalmente uma chamada de depuração em produção — o CI quebrará imediatamente.

Formato da saída

O compilador exibe os valores com tipo explícito:

| @compileLog output                              |
| comptime_int: 42                                |
| type: u32                                       |
| []const u8: "hello"                             |
| bool: true                                      |
error: compile log statement found
    src/main.zig:5:5: note: called from here

O formato exato pode variar entre versões do compilador Zig.

Diferença entre @compileLog e std.debug.print

Característica@compileLogstd.debug.print
Quando executaCompilaçãoRuntime
Tipos suportadosComptimeRuntime
Falha compilaçãoSimNão
Disponível em comptimeSimNão
Útil para depurarCódigo comptimeCódigo runtime

Técnica avançada: @compileLog com @src

Para identificar de onde o log veio em funções genéricas chamadas de vários lugares:

fn funcaoGenerica(comptime T: type) void {
    @compileLog(@src().fn_name, @src().line, @typeName(T));
    // Saída: | []const u8: "funcaoGenerica", comptime_int: 15, type: u32 |
}

Equivalente em C

Em C, não existe equivalente direto para mensagens em tempo de compilação com valores. #pragma message pode imprimir strings literais, mas não valores computados:

// C: apenas strings literais
#pragma message("Compilando módulo X")

// Zig: valores computados em comptime
@compileLog("Tamanho do tipo:", @sizeOf(MinhaStruct));

Limitações

  • Não aceita valores runtime: Se você tentar passar uma variável não-comptime, receberá um erro de compilação sobre “runtime value in comptime context”.
  • Saída não ordenada garantida: Em código com múltiplos @compileLog, a ordem de saída pode variar dependendo da ordem de avaliação comptime do compilador.
  • Não é uma API estável: O formato da saída pode mudar entre versões do Zig.

Perguntas Frequentes

Posso usar @compileLog em testes?

Sim, mas os testes falharão ao compilar. Isso pode ser útil durante desenvolvimento de testes — adicione, depure, remova.

Como inspecionar valores runtime com técnicas similares?

Para valores runtime, use std.debug.print (para saída no terminal) ou std.log (para logging estruturado). Para inspecionar durante testes, use std.debug.print ou verifique com try std.testing.expect.

Existe um modo “quiet” onde @compileLog não falha?

Não. O comportamento de falha é intencional e não pode ser desativado. Essa é uma decisão de design do Zig para evitar logs de debug em código de produção.

Builtins relacionados

  • @compileError — Gera erro de compilação com mensagem personalizada
  • @typeName — Obtém nome de tipo para exibição
  • @typeInfo — Inspeciona tipos em comptime
  • @src — Obtém localização no código-fonte

Tutoriais relacionados

Continue aprendendo Zig

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