Como Usar ArenaAllocator em Zig

Introdução

O ArenaAllocator é um dos alocadores mais úteis em Zig. Ele aloca memória de um alocador subjacente e libera tudo de uma vez quando o arena é destruído. Isso é perfeito para cenários onde várias alocações têm o mesmo tempo de vida – como parsing de dados, processamento de requisições ou construção de estruturas temporárias.

Nesta receita, você aprenderá quando e como usar o ArenaAllocator na prática.

Pré-requisitos

Uso Básico do ArenaAllocator

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    // Criar arena com GPA como alocador subjacente
    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit(); // Libera TUDO de uma vez

    const allocator = arena.allocator();

    // Alocar sem precisar de defer/free individual
    const nome = try allocator.dupe(u8, "Zig Brasil");
    const numeros = try allocator.alloc(u32, 100);
    const mensagem = try std.fmt.allocPrint(allocator, "Olá, {s}!", .{nome});

    // Usar normalmente
    for (numeros, 0..) |*n, i| {
        n.* = @intCast(i * 2);
    }

    std.debug.print("Nome: {s}\n", .{nome});
    std.debug.print("Mensagem: {s}\n", .{mensagem});
    std.debug.print("Primeiro número: {d}\n", .{numeros[0]});
    std.debug.print("Último número: {d}\n", .{numeros[99]});

    // Não precisa de free individual -- arena.deinit() cuida de tudo
}

Arena para Processamento de Dados

Ideal quando você processa dados em fases:

const std = @import("std");

const Registro = struct {
    nome: []const u8,
    valor: f64,
};

fn processarCSV(allocator: std.mem.Allocator, csv: []const u8) ![]Registro {
    var registros = std.ArrayList(Registro).init(allocator);

    var linhas = std.mem.splitScalar(u8, csv, '\n');
    while (linhas.next()) |linha| {
        if (linha.len == 0) continue;

        var campos = std.mem.splitScalar(u8, linha, ',');
        const nome_raw = campos.next() orelse continue;
        const valor_raw = campos.next() orelse continue;

        const nome = try allocator.dupe(u8, std.mem.trim(u8, nome_raw, " "));
        const valor = std.fmt.parseFloat(f64, std.mem.trim(u8, valor_raw, " ")) catch continue;

        try registros.append(.{ .nome = nome, .valor = valor });
    }

    return registros.toOwnedSlice();
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    // Arena para todo o processamento
    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit();

    const csv_data =
        \\Produto A, 29.90
        \\Produto B, 49.50
        \\Produto C, 15.00
        \\Produto D, 89.99
    ;

    const registros = try processarCSV(arena.allocator(), csv_data);

    std.debug.print("Registros processados:\n", .{});
    var total: f64 = 0;
    for (registros) |reg| {
        std.debug.print("  {s}: R$ {d:.2}\n", .{ reg.nome, reg.valor });
        total += reg.valor;
    }
    std.debug.print("Total: R$ {d:.2}\n", .{total});
    // arena.deinit() libera tudo automaticamente
}

Reset para Reutilização

Use reset para liberar toda a memória e reusar o arena:

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit();

    const allocator = arena.allocator();

    // Fase 1: Processar dados
    const dados1 = try allocator.alloc(u8, 1000);
    @memset(dados1, 'A');
    std.debug.print("Fase 1: Alocados {d} bytes\n", .{dados1.len});

    // Liberar tudo da fase 1 e começar de novo
    _ = arena.reset(.retain_capacity);

    // Fase 2: Novos dados (reutiliza a memória)
    const dados2 = try allocator.alloc(u8, 500);
    @memset(dados2, 'B');
    std.debug.print("Fase 2: Alocados {d} bytes\n", .{dados2.len});

    // Fase 3
    _ = arena.reset(.retain_capacity);
    const dados3 = try allocator.alloc(u8, 200);
    @memset(dados3, 'C');
    std.debug.print("Fase 3: Alocados {d} bytes\n", .{dados3.len});
}

Arena para Construir Strings

Construir strings sem se preocupar em liberar cada pedaço:

const std = @import("std");

fn construirRelatorio(allocator: std.mem.Allocator, itens: []const []const u8) ![]const u8 {
    var partes = std.ArrayList(u8).init(allocator);
    const writer = partes.writer();

    try writer.writeAll("=== RELATÓRIO ===\n\n");

    for (itens, 1..) |item, i| {
        try writer.print("{d}. {s}\n", .{ i, item });
    }

    try writer.print("\nTotal: {d} itens\n", .{itens.len});
    try writer.writeAll("=================\n");

    return partes.toOwnedSlice();
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit();

    const itens = [_][]const u8{
        "Estudar Zig",
        "Implementar servidor",
        "Escrever testes",
        "Revisar código",
    };

    const relatorio = try construirRelatorio(arena.allocator(), &itens);
    std.debug.print("{s}", .{relatorio});
}

Quando Usar ArenaAllocator

CenárioRecomendado?
Parsing de dadosSim
Processamento de requisição HTTPSim
Construção de strings temporáriasSim
Objetos com tempos de vida diferentesNão
Alocações de longa duraçãoNão
Cache em memóriaNão

Dicas e Boas Práticas

  1. Um defer arena.deinit() substitui muitos defer free(): Essa é a grande vantagem – simplificação do gerenciamento de memória.

  2. Use com GPA em desenvolvimento: ArenaAllocator.init(gpa.allocator()) permite detectar bugs durante o desenvolvimento.

  3. reset(.retain_capacity) reutiliza memória: Não libera para o OS, mas permite reutilizar os blocos alocados.

  4. Não misture tempos de vida: Se alguns dados precisam sobreviver mais que outros, use alocadores separados.

  5. Arena dentro de arena: Você pode criar sub-arenas para escopos mais curtos dentro de um processamento maior.

Receitas Relacionadas

Tutoriais Relacionados

Continue aprendendo Zig

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