@src em Zig — Referência e Exemplos

@src em Zig

O @src retorna uma struct com informações sobre a localização no código-fonte onde ele é invocado — incluindo o nome do arquivo, número da linha e coluna. Útil para logging, debug e mensagens de erro que precisam indicar de onde foram chamados.

Sintaxe

@src() std.builtin.SourceLocation

Parâmetros

Nenhum.

Valor de retorno

Retorna uma struct std.builtin.SourceLocation com os campos:

  • file ([:0]const u8): Caminho do arquivo-fonte
  • line (u32): Número da linha
  • column (u32): Número da coluna
  • fn_name ([:0]const u8): Nome da função onde @src() é invocado

Exemplos práticos

Exemplo 1: Logging com localização

const std = @import("std");

fn log(comptime nivel: []const u8, mensagem: []const u8, loc: std.builtin.SourceLocation) void {
    std.debug.print("[{s}] {s}:{d} ({s}): {s}\n", .{
        nivel,
        loc.file,
        loc.line,
        loc.fn_name,
        mensagem,
    });
}

fn processar() void {
    log("INFO", "Iniciando processamento", @src());
    // [INFO] src/main.zig:15 (processar): Iniciando processamento

    log("WARN", "Dados incompletos", @src());
}

pub fn main() void {
    log("INFO", "Programa iniciado", @src());
    processar();
}

Exemplo 2: Assert personalizado com localização

const std = @import("std");

fn meuAssert(ok: bool, mensagem: []const u8, loc: std.builtin.SourceLocation) void {
    if (!ok) {
        std.debug.print("ASSERTION FAILED em {s}:{d} ({s}): {s}\n", .{
            loc.file,
            loc.line,
            loc.fn_name,
            mensagem,
        });
        @panic("Assertion failed");
    }
}

pub fn main() void {
    const x: u32 = 42;
    meuAssert(x > 0, "x deve ser positivo", @src());
    meuAssert(x < 100, "x deve ser menor que 100", @src());
}

Exemplo 3: Rastreamento de chamadas

const std = @import("std");

const Tracer = struct {
    pub fn entrar(loc: std.builtin.SourceLocation) void {
        std.debug.print("--> {s} ({s}:{d})\n", .{ loc.fn_name, loc.file, loc.line });
    }

    pub fn sair(loc: std.builtin.SourceLocation) void {
        std.debug.print("<-- {s}\n", .{loc.fn_name});
    }
};

fn calcular() u32 {
    Tracer.entrar(@src());
    defer Tracer.sair(@src());

    return 42;
}

pub fn main() void {
    Tracer.entrar(@src());
    defer Tracer.sair(@src());

    const resultado = calcular();
    std.debug.print("Resultado: {}\n", .{resultado});
}

Casos de uso comuns

  1. Logging estruturado: Incluir arquivo e linha nas mensagens de log automaticamente.
  2. Assertions personalizados: Mensagens de erro que indicam exatamente onde a falha ocorreu.
  3. Profiling: Rastrear entradas e saídas de funções.
  4. Debug: Identificar o ponto exato de execução durante debugging.

Como @src funciona internamente

O @src é resolvido inteiramente em tempo de compilação (comptime). O compilador substitui @src() por uma struct literal com os valores do arquivo, linha, coluna e nome da função no ponto exato da chamada. Não há overhead em runtime — os dados estão embutidos no binário como constantes.

Isso contrasta com soluções em C que dependem de macros (__FILE__, __LINE__) ou bibliotecas externas para obter informações de debug. Em Zig, é uma funcionalidade de primeira classe da linguagem.

Padrão idiomático: passar @src() como argumento

A forma mais idiomática de usar @src é que a função chamadora passa @src() como argumento, e não que a função interna chama @src(). Isso garante que a localização registrada seja do ponto de chamada, não da implementação:

const std = @import("std");

fn registrarErro(mensagem: []const u8, src: std.builtin.SourceLocation) void {
    std.debug.print("[ERRO] {s}:{d}:{d} em `{s}`: {s}\n", .{
        src.file,
        src.line,
        src.column,
        src.fn_name,
        mensagem,
    });
}

fn validarIdade(idade: u8) void {
    if (idade > 150) {
        registrarErro("Idade inválida", @src()); // aponta para esta linha
    }
}

Se registrarErro chamasse @src() internamente, o arquivo e linha apontariam para dentro de registrarErro, não para onde o erro foi detectado — muito menos útil para debugging.

Integração com sistemas de log

O campo fn_name é especialmente valioso para logs estruturados, pois identifica automaticamente qual função registrou cada mensagem:

const std = @import("std");

const Nivel = enum { debug, info, warn, erro };

fn log(nivel: Nivel, comptime fmt: []const u8, args: anytype, src: std.builtin.SourceLocation) void {
    const prefixo = switch (nivel) {
        .debug => "DEBUG",
        .info  => "INFO ",
        .warn  => "WARN ",
        .erro  => "ERRO ",
    };
    std.debug.print("[{s}] {s}:{d} | " ++ fmt ++ "\n",
        .{ prefixo, src.fn_name, src.line } ++ args);
}

// Uso:
fn inicializar() !void {
    log(.info, "Inicializando módulo", .{}, @src());
    // [INFO ] inicializar:42 | Inicializando módulo
}

Comparação com equivalente em C

Em C, obter informações de localização requer macros do pré-processador:

// C: usa macros especiais do pré-processador
#define LOG(msg) printf("[%s:%d] %s\n", __FILE__, __LINE__, msg)
// __FUNCTION__ ou __func__ para o nome da função

As macros do C são substituídas em pré-processamento e não são valores de primeira classe — não podem ser passadas como argumentos de função de forma elegante. Em Zig, std.builtin.SourceLocation é uma struct normal que pode ser armazenada, passada, comparada e usada em qualquer lugar:

// Zig: SourceLocation é um valor de primeira classe
const loc = @src();
armazenarParaDepuracao(loc);

Erros comuns

1. Chamar @src() dentro da função de log, não no ponto de chamada:

// ERRADO: location aponta para dentro de `meuLog`, não para o chamador
fn meuLog(msg: []const u8) void {
    const loc = @src(); // sempre aponta para esta linha
    std.debug.print("{s}:{d}: {s}\n", .{ loc.file, loc.line, msg });
}

// CORRETO: o chamador passa @src()
fn meuLog(msg: []const u8, loc: std.builtin.SourceLocation) void {
    std.debug.print("{s}:{d}: {s}\n", .{ loc.file, loc.line, msg });
}
// No chamador: meuLog("mensagem", @src());

Perguntas Frequentes

P: O campo file retorna o caminho completo ou apenas o nome do arquivo?

R: Retorna o caminho relativo ao diretório raiz do projeto (onde o build.zig está). Por exemplo, src/main.zig ou lib/utils.zig. O formato exato pode variar conforme a configuração do projeto.

P: @src() pode ser usado em comptime?

R: Sim. Como @src é resolvido em comptime, pode ser usado em blocos comptime e em funções comptime. O fn_name refletirá o contexto comptime.

P: O coluna retornada é precisa?

R: Sim — a coluna aponta para o início do token @src na linha correspondente. Isso é útil para distinguir múltiplas chamadas na mesma linha, embora seja raro na prática.

Builtins relacionados

Tutoriais relacionados

Continue aprendendo Zig

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