---
title: "Como Usar Pool Allocators em Zig"
url: "https://ziglang.com.br/receitas/como-usar-pool-allocators-em-zig/"
markdown_url: "https://ziglang.com.br/receitas/como-usar-pool-allocators-em-zig.MD"
description: "Aprenda a usar pool allocators em Zig para alocação eficiente de objetos de tamanho fixo. Ideal para nós de listas, partículas e objetos frequentemente criados e destruídos."
date: "2026-02-21"
author: "Zig Brasil"
---

# Como Usar Pool Allocators em Zig

Aprenda a usar pool allocators em Zig para alocação eficiente de objetos de tamanho fixo. Ideal para nós de listas, partículas e objetos frequentemente criados e destruídos.


## Introdução

Um pool allocator (alocador de pool) pré-aloca blocos de memória de tamanho fixo e os reutiliza conforme necessário. Isso elimina a fragmentação e torna a alocação/desalocação extremamente rápida (O(1)). Pools são ideais para cenários onde muitos objetos do mesmo tipo são frequentemente criados e destruídos, como partículas em jogos, nós de grafos ou conexões de rede.

Nesta receita, você aprenderá a implementar e usar pool allocators em Zig.

## Pré-requisitos

- Zig instalado (versão 0.13+). Veja o [guia de instalação](/tutoriais/como-instalar-zig/)
- Conhecimento básico de Zig. Consulte a [introdução ao Zig](/tutoriais/introducao-ao-zig/)
- Familiaridade com [alocadores](/receitas/zig-general-purpose-allocator/)

## Implementação de Pool Simples

Pool de objetos de tamanho fixo com lista livre:

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

fn Pool(comptime T: type, comptime pool_size: usize) type {
    return struct {
        const Self = @This();

        const Node = struct {
            data: T,
            next_free: ?*Node,
        };

        nodes: [pool_size]Node = undefined,
        free_list: ?*Node = null,
        alocados: usize = 0,

        pub fn init() Self {
            var self = Self{};
            // Construir lista livre
            var i: usize = pool_size;
            while (i > 0) {
                i -= 1;
                self.nodes[i].next_free = self.free_list;
                self.free_list = &self.nodes[i];
            }
            return self;
        }

        pub fn create(self: *Self) !*T {
            const node = self.free_list orelse return error.PoolExhausted;
            self.free_list = node.next_free;
            self.alocados += 1;
            node.data = undefined;
            return &node.data;
        }

        pub fn destroy(self: *Self, ptr: *T) void {
            const node: *Node = @fieldParentPtr("data", ptr);
            node.next_free = self.free_list;
            self.free_list = node;
            self.alocados -= 1;
        }

        pub fn disponivel(self: *Self) usize {
            return pool_size - self.alocados;
        }
    };
}

const Particula = struct {
    x: f32,
    y: f32,
    vx: f32,
    vy: f32,
    vida: f32,
};

pub fn main() !void {
    var pool = Pool(Particula, 100).init();

    // Criar partículas
    var particulas: [10]*Particula = undefined;
    for (&particulas, 0..) |*p, i| {
        p.* = try pool.create();
        const fi: f32 = @floatFromInt(i);
        p.*.* = .{
            .x = fi * 10.0,
            .y = fi * 5.0,
            .vx = 1.0,
            .vy = -0.5,
            .vida = 100.0,
        };
    }

    std.debug.print("Partículas criadas: {d}\n", .{pool.alocados});
    std.debug.print("Disponíveis: {d}\n", .{pool.disponivel()});

    // Simular: destruir algumas
    for (particulas[0..5]) |p| {
        pool.destroy(p);
    }
    std.debug.print("Após destruir 5: {d} alocadas, {d} disponíveis\n", .{
        pool.alocados,
        pool.disponivel(),
    });

    // Criar novas (reutiliza a memória)
    const nova = try pool.create();
    nova.* = .{ .x = 0, .y = 0, .vx = 2, .vy = 2, .vida = 200 };
    std.debug.print("Nova partícula criada. Total: {d}\n", .{pool.alocados});
}
```

### Saída esperada

```
Partículas criadas: 10
Disponíveis: 90
Após destruir 5: 5 alocadas, 95 disponíveis
Nova partícula criada. Total: 6
```

## Pool Dinâmico com Alocador

Pool que pode crescer usando um alocador subjacente:

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

fn DynamicPool(comptime T: type) type {
    return struct {
        const Self = @This();

        items: std.ArrayList(T),
        free_indices: std.ArrayList(usize),

        pub fn init(allocator: std.mem.Allocator) Self {
            return .{
                .items = std.ArrayList(T).init(allocator),
                .free_indices = std.ArrayList(usize).init(allocator),
            };
        }

        pub fn deinit(self: *Self) void {
            self.items.deinit();
            self.free_indices.deinit();
        }

        pub fn acquire(self: *Self) !usize {
            if (self.free_indices.popOrNull()) |idx| {
                return idx;
            }
            // Sem índice livre, expandir
            try self.items.append(undefined);
            return self.items.items.len - 1;
        }

        pub fn release(self: *Self, idx: usize) !void {
            try self.free_indices.append(idx);
        }

        pub fn get(self: *Self, idx: usize) *T {
            return &self.items.items[idx];
        }

        pub fn ativas(self: *Self) usize {
            return self.items.items.len - self.free_indices.items.len;
        }
    };
}

const Conexao = struct {
    id: u32,
    ativa: bool,
    endereco: []const u8,
};

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

    var pool = DynamicPool(Conexao).init(allocator);
    defer pool.deinit();

    // Simular conexões
    const idx1 = try pool.acquire();
    pool.get(idx1).* = .{ .id = 1, .ativa = true, .endereco = "192.168.1.1" };

    const idx2 = try pool.acquire();
    pool.get(idx2).* = .{ .id = 2, .ativa = true, .endereco = "192.168.1.2" };

    const idx3 = try pool.acquire();
    pool.get(idx3).* = .{ .id = 3, .ativa = true, .endereco = "192.168.1.3" };

    std.debug.print("Conexões ativas: {d}\n", .{pool.ativas()});

    // Liberar uma conexão
    try pool.release(idx2);
    std.debug.print("Após liberar idx2: {d} ativas\n", .{pool.ativas()});

    // Reutilizar slot
    const idx4 = try pool.acquire(); // Reutiliza idx2
    pool.get(idx4).* = .{ .id = 4, .ativa = true, .endereco = "192.168.1.4" };
    std.debug.print("Nova conexão no slot {d}: {s}\n", .{ idx4, pool.get(idx4).endereco });
}
```

## Quando Usar Pool Allocator

| Cenário | Pool? | Por quê? |
|---|---|---|
| Partículas em jogos | Sim | Criação/destruição frequente, tamanho fixo |
| Nós de listas/grafos | Sim | Muitos objetos iguais |
| Conexões de rede | Sim | Pool de conexões é padrão |
| Strings de tamanho variável | Não | Use ArenaAllocator |
| Dados de longa duração | Não | Use GPA |

## Dicas e Boas Práticas

1. **Pool fixo para performance máxima**: Sem alocação dinâmica, tempo constante garantido.

2. **Pool dinâmico para flexibilidade**: Quando o número máximo não é conhecido antecipadamente.

3. **Reutilize sempre**: Pools brilham quando objetos são criados e destruídos frequentemente.

4. **Inicialize ao adquirir**: Sempre defina valores ao adquirir um slot do pool, pois pode conter dados antigos.

5. **Combine com ArenaAllocator**: Para objetos com dados de tamanho variável, use arena para os dados e pool para os objetos.

## Receitas Relacionadas

- [Usando ArenaAllocator](/receitas/zig-arena-allocator/) - Alocação em lote
- [Usando GeneralPurposeAllocator](/receitas/zig-general-purpose-allocator/) - Alocador geral
- [Usando FixedBufferAllocator](/receitas/zig-fixed-buffer-allocator/) - Buffer fixo
- [Operações com Lista Ligada](/receitas/zig-linked-list/) - Nós com pool

## Tutoriais Relacionados

- [Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/)
- [Introdução ao Zig](/tutoriais/introducao-ao-zig/)
