Alocadores: O Poder do Controle Manual
Por que 'malloc' é ruim e como Arenas podem acelerar seu código em 10x.
Em C, usa-se malloc global. Em Rust, o alocador global é o padrão.
Em Zig, não existe alocador global oculto. Se uma função aloca memória, ela DEVE receber um Allocator por parâmetro.
// Assinatura honesta: Sei que essa função pode falhar (memória cheia)
fn concatena(allocator: Allocator, a: []u8, b: []u8) ![]u8 {
const result = try allocator.alloc(u8, a.len + b.len);
// ... copia ...
return result;
}
Por que passar alocadores é genial?
Porque você pode escolher a estratégia certa para cada contexto.
1. General Purpose Allocator (GPA)
O padrão para Desktop. Detecta leaks, double free e use-after-free em Debug. Lento, mas seguro.
2. Fixed Buffer Allocator (Stack/Static)
Hiper-rápido. Usa um array fixo de bytes como “memória ram”. Zero syscalls (sbrk/mmap). Ideal para embarcados críticos.
var buffer: [1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
// Alocações aqui caem direto no buffer da stack!
const lista = try allocator.alloc(i32, 10);
3. Arena Allocator (O Mágico)
Aloca tudo em blocos contínuos. Quando você termina, libera TUDO de uma vez (deinit).
Cenário: Um servidor Web processando uma requisição JSON.
- Parser JSON cria milhares de pequenos nós (tokens, strings).
- Dar
freeem cada nó é lento e propenso a erro. - Com Arena: Dê um
arena.deinit()no fim da requisição. O custo de free vira O(1).
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // Libera TUDO no fim do escopo
const allocator = arena.allocator();
// Faça 1000 alocações... nem se preocupe em liberar individualmente.
// A arena limpa tudo de uma vez.
Essa técnica é comum em games (Frame Allocator) e compiladores, e o Zig torna ela trivial.