@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
- Logging estruturado: Incluir arquivo e linha nas mensagens de log automaticamente.
- Assertions personalizados: Mensagens de erro que indicam exatamente onde a falha ocorreu.
- Profiling: Rastrear entradas e saídas de funções.
- 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
- @compileLog — Imprime valores em tempo de compilação
- @compileError — Gera erro com mensagem em comptime
- @panic — Encerra execução com mensagem
- @typeName — Nome de tipo para logging