Inline em Zig — O que é e Como Usar
Definição
A palavra-chave inline em Zig serve para duas finalidades principais: forçar o inlining de funções e desenrolar loops em tempo de compilação. Quando aplicada a uma função, o corpo da função é substituído diretamente no local da chamada. Quando aplicada a um for ou while, o loop é desenrolado (unrolled), e cada iteração é “colada” sequencialmente no código gerado.
Por que Inline Importa
- Performance: Elimina o custo de chamada de função (push/pop de stack frame, salto).
- Comptime em loops:
inline forpermite iterar sobre tipos e valores em tempo de compilação. - Otimização manual: Quando o compilador não faz inline automaticamente, você pode forçar.
- Metaprogramação: Combinado com comptime, permite gerar código especializado.
Exemplo Prático
Inline For (Desenrolamento de Loop)
const std = @import("std");
fn somaVetorial(vetor: @Vector(4, f32)) f32 {
// Desenrola em 4 somas individuais em tempo de compilação
var resultado: f32 = 0;
const campos = [_]comptime_int{ 0, 1, 2, 3 };
inline for (campos) |i| {
resultado += vetor[i];
}
return resultado;
}
pub fn main() void {
const v: @Vector(4, f32) = .{ 1.0, 2.0, 3.0, 4.0 };
std.debug.print("Soma: {d}\n", .{somaVetorial(v)}); // 10.0
}
Inline For com Tipos (Metaprogramação)
const std = @import("std");
fn imprimir(args: anytype) void {
inline for (std.meta.fields(@TypeOf(args))) |campo| {
std.debug.print("{s}: {}\n", .{ campo.name, @field(args, campo.name) });
}
}
pub fn main() void {
imprimir(.{ .nome = "Zig", .versao = 13, .estavel = true });
}
Inline Function
// Forçar inlining — o corpo será copiado no local da chamada
inline fn max(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
return if (a > b) a else b;
}
pub fn main() void {
// Não há chamada de função no código gerado
const resultado = max(@as(u32, 10), @as(u32, 20));
std.debug.print("Max: {}\n", .{resultado});
}
Inline While
fn contar(comptime n: usize) [n]usize {
var resultado: [n]usize = undefined;
comptime var i: usize = 0;
inline while (i < n) : (i += 1) {
resultado[i] = i * i;
}
return resultado;
}
const quadrados = contar(5); // [0, 1, 4, 9, 16]
Inline vs Comptime
| Aspecto | inline | comptime |
|---|---|---|
| Funções | Substitui corpo no local da chamada | Executa inteiramente na compilação |
| Loops | Desenrola iterações | Avalia valores em compilação |
| Resultado | Código mais rápido, binário maior | Constante embutida no binário |
Na prática, inline for frequentemente trabalha em conjunto com valores comptime:
inline for (comptime std.meta.fieldNames(MeuTipo)) |nome| {
// cada iteração é resolvida em compilação
}
Armadilhas Comuns
- Binário inchado: Funções inline muito grandes, chamadas em muitos lugares, aumentam o tamanho do binário significativamente.
- Inline desnecessário: O compilador de Zig (via LLVM) já faz inlining automático quando vantajoso. Use
inlineexplícito apenas quando necessário. - Confundir
inline forcomfornormal:inline forrequer que o iterável seja conhecido em comptime. Um slice dinâmico não pode ser usado cominline for. - Debug mais difícil: Código inline não aparece como chamada de função separada no stack trace.
Quando Usar Inline
A escolha de quando aplicar inline manualmente deve ser cuidadosa. O compilador Zig (via LLVM) já realiza inlining automático em muitos casos, especialmente em modos de otimização como ReleaseFast. Use inline explícito apenas nas situações abaixo:
inline fn: Quando uma função pequena e crítica para performance precisa ser embutida independentemente do modo de compilação.inline for: Quando você precisa iterar sobre uma tupla, lista de tipos ou valores conhecidos em comptime — pois loops normais não podem iterar sobre tipos.inline while: Quando cada iteração precisa gerar código especializado diferente, ou quando a condição envolve um valor comptime.
Um caso de uso muito comum de inline for é implementar funções que aceitam anytype e precisam inspecionar os campos de uma struct em tempo de compilação:
const std = @import("std");
fn serializar(writer: anytype, valor: anytype) !void {
const T = @TypeOf(valor);
inline for (std.meta.fields(T)) |campo| {
const v = @field(valor, campo.name);
try writer.print("{s}={}\n", .{ campo.name, v });
}
}
const Config = struct {
porta: u16,
debug: bool,
versao: u32,
};
pub fn main() !void {
const cfg = Config{ .porta = 8080, .debug = true, .versao = 2 };
const writer = std.io.getStdOut().writer();
try serializar(writer, cfg);
}
Comparação com Outras Linguagens
Em C e C++, a palavra-chave inline é apenas uma sugestão para o compilador — que pode ignorá-la. Em Zig, inline fn é uma garantia: o compilador é obrigado a embutir o corpo da função. Para loops, C/C++ não possuem equivalente nativo a inline for; o mais próximo seria macros ou templates, que são muito mais verbosos e menos seguros. Em Rust, o atributo #[inline(always)] tem comportamento semelhante ao inline fn do Zig.
Termos Relacionados
- Comptime — Execução em tempo de compilação
- Release Modes — Modos de compilação e otimização
- Vector/SIMD — Tipos vetoriais otimizados