@call em Zig
O @call invoca uma função com controle avançado sobre como a chamada é realizada. Permite forçar inlining, prevenir tail calls, controlar otimizações de chamada e especificar o stack frame. Útil para código de alta performance, recursão e interoperabilidade.
Sintaxe
@call(modifier: std.builtin.CallModifier, function: anytype, args: anytype) ReturnType
Parâmetros
- modifier (
CallModifier): Controle sobre como a chamada é feita. - function: A função a ser chamada.
- args: Tupla com os argumentos da função.
Valor de retorno
Retorna o mesmo tipo que a função chamada retornaria.
Modifiers disponíveis
| Modifier | Descrição |
|---|---|
.auto | Comportamento padrão (igual a chamada normal) |
.always_inline | Forçar inlining (erro se não for possível) |
.never_inline | Nunca inline (sempre chamada de função) |
.never_tail | Nunca otimizar como tail call |
.compile_time | Forçar avaliação em comptime |
Exemplos práticos
Exemplo 1: Forçar inlining
const std = @import("std");
fn somaRapida(a: u64, b: u64) u64 {
return a + b;
}
pub fn main() void {
// Forçar que somaRapida seja inlined
const resultado = @call(.always_inline, somaRapida, .{
@as(u64, 42),
@as(u64, 58),
});
std.debug.print("Soma: {}\n", .{resultado}); // 100
}
Exemplo 2: Prevenir tail call em recursão
const std = @import("std");
fn fatorial(n: u64) u64 {
if (n <= 1) return 1;
// Prevenir tail call para manter stack frame (útil para debug)
return n * @call(.never_tail, fatorial, .{n - 1});
}
pub fn main() void {
std.debug.print("10! = {}\n", .{fatorial(10)}); // 3628800
}
Exemplo 3: Chamada genérica de função
const std = @import("std");
fn chamarComLog(funcao: anytype, args: anytype) @TypeOf(@call(.auto, funcao, args)) {
std.debug.print("Chamando função...\n", .{});
const resultado = @call(.auto, funcao, args);
std.debug.print("Função retornou.\n", .{});
return resultado;
}
fn multiplicar(a: i32, b: i32) i32 {
return a * b;
}
fn saudar(nome: []const u8) void {
std.debug.print("Olá, {s}!\n", .{nome});
}
pub fn main() void {
const produto = chamarComLog(multiplicar, .{ @as(i32, 6), @as(i32, 7) });
std.debug.print("Produto: {}\n", .{produto}); // 42
chamarComLog(saudar, .{"Zig Brasil"});
}
Casos de uso comuns
- Performance: Forçar inlining de funções críticas em hot paths.
- Debugging: Prevenir tail calls para manter stack traces completos.
- Wrappers genéricos: Criar decoradores/interceptadores de funções.
- Benchmarks: Prevenir que o compilador otimize chamadas de teste.
- Recursão controlada: Garantir que recursão use o stack conforme esperado.
@call vs chamada normal
// Equivalentes:
const r1 = funcao(a, b);
const r2 = @call(.auto, funcao, .{ a, b });
// Mas @call permite controle extra:
const r3 = @call(.always_inline, funcao, .{ a, b }); // Forçar inline
const r4 = @call(.never_tail, funcao, .{ a, b }); // Sem tail call
Builtins relacionados
- @import — Importar módulos
- @typeInfo — Introspecção para chamadas genéricas
- @TypeOf — Obter tipo de retorno de funções
- @compileError — Erro se inlining impossível