---
title: "Arena Allocator em Zig: Guia Prático com Exemplos Reais"
url: "https://ziglang.com.br/tutoriais/arena-allocator-em-zig-guia-pr%C3%A1tico-com-exemplos-reais/"
markdown_url: "https://ziglang.com.br/tutoriais/arena-allocator-em-zig-guia-pr%C3%A1tico-com-exemplos-reais.MD"
description: "Domine o Arena Allocator em Zig com exemplos práticos: processamento de dados, parsers, servidores HTTP e mais. Tutorial completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Arena Allocator em Zig: Guia Prático com Exemplos Reais

Domine o Arena Allocator em Zig com exemplos práticos: processamento de dados, parsers, servidores HTTP e mais. Tutorial completo em português.


O Arena Allocator é provavelmente o padrão de alocação mais poderoso em Zig. No [artigo anterior](/tutoriais/zig-memoria-masterclass/artigo-2-allocators-tipos/) apresentamos brevemente os allocators. Agora vamos explorar o Arena Allocator em profundidade, com exemplos práticos que você pode aplicar imediatamente nos seus projetos.

## O Que é um Arena Allocator?

Um Arena Allocator agrupa múltiplas alocações em blocos grandes e as libera **todas de uma vez**. Em vez de rastrear cada alocação individual, você simplesmente descarta a arena inteira quando terminar.

Pense nisso como um quadro branco: você escreve o quanto quiser, e quando termina, apaga tudo de uma vez — sem precisar apagar cada palavra individualmente.

```zig
const std = @import("std");

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

    // Criar arena com backing allocator
    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit(); // Uma única chamada libera TUDO

    const alloc = arena.allocator();

    // Muitas alocações — nenhum defer individual necessário
    const nome = try alloc.alloc(u8, 100);
    const sobrenome = try alloc.alloc(u8, 100);
    const numeros = try alloc.alloc(i32, 50);
    const buffer = try alloc.alloc(u8, 4096);

    @memcpy(nome[0..3], "Zig");
    @memcpy(sobrenome[0..6], "Brasil");
    numeros[0] = 42;
    buffer[0] = 0xFF;

    std.debug.print("Tudo alocado sem nenhum defer individual!\n", .{});
    std.debug.print("Nome: {s}, Sobrenome: {s}\n", .{ nome[0..3], sobrenome[0..6] });
    // arena.deinit() no defer libera nome, sobrenome, numeros e buffer
}
```

## Caso de Uso 1: Processamento de Requisições HTTP

O exemplo clássico de arena allocator é o processamento de requisições HTTP. Cada requisição aloca memória temporária, e tudo é liberado quando a requisição termina.

```zig
const std = @import("std");

const HttpRequest = struct {
    method: []const u8,
    path: []const u8,
    headers: std.StringHashMap([]const u8),
    body: []const u8,
};

const HttpResponse = struct {
    status: u16,
    body: []const u8,
};

fn parseRequest(allocator: std.mem.Allocator, raw: []const u8) !HttpRequest {
    var headers = std.StringHashMap([]const u8).init(allocator);

    // Simular parsing — todas as alocações usam o allocator da arena
    const method = try allocator.alloc(u8, 3);
    @memcpy(method, "GET");

    const path = try std.fmt.allocPrint(allocator, "/api/users", .{});

    try headers.put(
        try std.fmt.allocPrint(allocator, "Content-Type", .{}),
        try std.fmt.allocPrint(allocator, "application/json", .{}),
    );

    _ = raw;

    return HttpRequest{
        .method = method,
        .path = path,
        .headers = headers,
        .body = "",
    };
}

fn handleRequest(allocator: std.mem.Allocator, request: HttpRequest) !HttpResponse {
    const body = try std.fmt.allocPrint(
        allocator,
        "{{\"message\": \"Olá do {s} {s}\"}}",
        .{ request.method, request.path },
    );

    return HttpResponse{
        .status = 200,
        .body = body,
    };
}

fn processarRequisicao(backing_allocator: std.mem.Allocator, raw_request: []const u8) !void {
    // Arena para esta requisição — tudo é liberado ao final
    var arena = std.heap.ArenaAllocator.init(backing_allocator);
    defer arena.deinit();

    const alloc = arena.allocator();

    const request = try parseRequest(alloc, raw_request);
    const response = try handleRequest(alloc, request);

    std.debug.print("Status: {d}\n", .{response.status});
    std.debug.print("Body: {s}\n", .{response.body});

    // Ao sair desta função, arena.deinit() libera TODA a memória
    // de parsing, headers, body de resposta — tudo de uma vez
}

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

    // Simular várias requisições
    for (0..3) |i| {
        std.debug.print("\n--- Requisição {d} ---\n", .{i + 1});
        try processarRequisicao(gpa.allocator(), "GET /api/users HTTP/1.1\r\n");
    }
}
```

Cada requisição cria sua própria arena e, ao terminar, toda a memória é liberada com uma única operação. Isso é mais rápido do que liberar cada alocação individualmente e elimina a possibilidade de memory leaks por alocações esquecidas.

## Caso de Uso 2: Parser de Configuração

Parsers são outro caso ideal para arenas, pois geram muitas strings e estruturas intermediárias:

```zig
const std = @import("std");

const ConfigValue = union(enum) {
    string: []const u8,
    integer: i64,
    boolean: bool,
};

const Config = struct {
    valores: std.StringHashMap(ConfigValue),
    allocator: std.mem.Allocator,

    fn init(allocator: std.mem.Allocator) Config {
        return .{
            .valores = std.StringHashMap(ConfigValue).init(allocator),
            .allocator = allocator,
        };
    }

    fn set(self: *Config, chave: []const u8, valor: ConfigValue) !void {
        const chave_copia = try self.allocator.alloc(u8, chave.len);
        @memcpy(chave_copia, chave);
        try self.valores.put(chave_copia, valor);
    }

    fn getString(self: *const Config, chave: []const u8) ?[]const u8 {
        if (self.valores.get(chave)) |val| {
            switch (val) {
                .string => |s| return s,
                else => return null,
            }
        }
        return null;
    }

    fn getInt(self: *const Config, chave: []const u8) ?i64 {
        if (self.valores.get(chave)) |val| {
            switch (val) {
                .integer => |i| return i,
                else => return null,
            }
        }
        return null;
    }
};

fn parseConfig(allocator: std.mem.Allocator, fonte: []const u8) !Config {
    var config = Config.init(allocator);

    // Simular parsing de um arquivo de configuração
    _ = fonte;

    const host = try std.fmt.allocPrint(allocator, "localhost", .{});
    try config.set("host", .{ .string = host });
    try config.set("port", .{ .integer = 8080 });
    try config.set("debug", .{ .boolean = true });

    const db_url = try std.fmt.allocPrint(allocator, "postgres://localhost:5432/app", .{});
    try config.set("database_url", .{ .string = db_url });

    return config;
}

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

    // Arena para toda a configuração
    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit();

    var config = try parseConfig(arena.allocator(), "host=localhost\nport=8080\n");

    if (config.getString("host")) |host| {
        std.debug.print("Host: {s}\n", .{host});
    }
    if (config.getInt("port")) |port| {
        std.debug.print("Port: {d}\n", .{port});
    }
    if (config.getString("database_url")) |url| {
        std.debug.print("DB: {s}\n", .{url});
    }
}
```

## Caso de Uso 3: Processamento de Dados em Lote

Quando você processa dados em lotes, pode criar uma arena por lote e resetá-la entre iterações:

```zig
const std = @import("std");

const Estatisticas = struct {
    media: f64,
    max: f64,
    min: f64,
    total: f64,
};

fn processarLote(
    allocator: std.mem.Allocator,
    dados_brutos: []const f64,
) !Estatisticas {
    // Copiar e processar dados usando arena
    const dados = try allocator.alloc(f64, dados_brutos.len);
    @memcpy(dados, dados_brutos);

    // Ordenar (precisaria de buffer auxiliar na arena também)
    std.mem.sort(f64, dados, {}, std.sort.asc(f64));

    var total: f64 = 0;
    for (dados) |v| {
        total += v;
    }

    return Estatisticas{
        .media = total / @as(f64, @floatFromInt(dados.len)),
        .max = dados[dados.len - 1],
        .min = dados[0],
        .total = total,
    };
}

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

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

    // Simular vários lotes de dados
    const lotes = [_][]const f64{
        &.{ 3.2, 1.5, 4.7, 2.8, 6.1 },
        &.{ 10.0, 20.0, 15.0, 25.0 },
        &.{ 100.5, 200.3, 50.1, 75.8, 150.2, 300.0 },
    };

    for (lotes, 1..) |lote, i| {
        // Reset da arena entre lotes — libera memória do lote anterior
        _ = arena.reset(.retain_capacity);

        const stats = try processarLote(arena.allocator(), lote);
        std.debug.print("Lote {d}: média={d:.2}, min={d:.2}, max={d:.2}, total={d:.2}\n", .{
            i, stats.media, stats.min, stats.max, stats.total,
        });
    }
}
```

O `reset(.retain_capacity)` libera todas as alocações mas mantém a memória reservada, evitando idas ao sistema operacional nos próximos lotes.

## Arena Scoped: Padrão de Escopo Temporário

Uma técnica poderosa é criar sub-escopos com arenas temporárias:

```zig
const std = @import("std");

fn operacaoCompleta(permanent_alloc: std.mem.Allocator) ![]u8 {
    // Arena temporária para cálculos intermediários
    var temp_arena = std.heap.ArenaAllocator.init(permanent_alloc);
    defer temp_arena.deinit();

    const temp = temp_arena.allocator();

    // Dados intermediários — serão liberados automaticamente
    const buffer1 = try temp.alloc(u8, 1000);
    const buffer2 = try temp.alloc(u8, 2000);
    _ = buffer2;

    for (buffer1, 0..) |*b, i| {
        b.* = @intCast(i % 256);
    }

    // Resultado final — alocado no allocator permanente
    const resultado = try permanent_alloc.alloc(u8, 10);
    @memcpy(resultado, buffer1[0..10]);

    return resultado;
    // temp_arena.deinit() libera buffer1 e buffer2 automaticamente
    // resultado sobrevive porque foi alocado no permanent_alloc
}

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

    const resultado = try operacaoCompleta(gpa.allocator());
    defer gpa.allocator().free(resultado);

    std.debug.print("Resultado: {any}\n", .{resultado});
}
```

## Melhores Práticas com Arena Allocator

1. **Use arena por unidade de trabalho** — Uma requisição HTTP, um frame de jogo, um lote de processamento
2. **Prefira `reset(.retain_capacity)`** em loops — Evita syscalls repetidas
3. **Combine com GPA para debugging** — Passe GPA como backing allocator durante desenvolvimento
4. **Não libere individualmente** — O objetivo da arena é liberar tudo junto; `free()` individual é no-op

## Resumo

| Padrão | Quando Usar |
|--------|-------------|
| Arena por requisição | Servidores HTTP, processamento de mensagens |
| Arena por lote | Pipeline de dados, ETL |
| Arena temporária | Cálculos intermediários dentro de uma operação |
| Arena com reset | Loops de processamento com padrão repetitivo |

No próximo artigo, vamos aprender a **[criar custom allocators](/tutoriais/zig-memoria-masterclass/artigo-4-custom-allocator/)** para necessidades específicas da sua aplicação.

## Leitura Complementar

- [Tipos de Allocators (Artigo 2)](/tutoriais/zig-memoria-masterclass/artigo-2-allocators-tipos/) — Artigo anterior
- [Performance com Zig](/tutoriais/zig-performance/) — Otimizações que se beneficiam de arenas
- [Desenvolvimento Web com Zig](/tutoriais/zig-web-development/) — Arenas em servidores HTTP
