std.ArrayList em Zig — Referência e Exemplos

std.ArrayList — Array Dinâmico

O std.ArrayList é a estrutura de dados de array dinâmico da biblioteca padrão do Zig. Ele funciona como um vetor que cresce automaticamente conforme novos elementos são adicionados, gerenciando a alocação de memória de forma eficiente com crescimento exponencial. É o equivalente ao std::vector do C++ ou ao Vec do Rust.

Visão Geral

const std = @import("std");
const ArrayList = std.ArrayList;

O ArrayList é um tipo genérico parametrizado pelo tipo dos elementos. Internamente, ele mantém um ponteiro para um bloco de memória contígua, um comprimento (número de elementos inseridos) e uma capacidade (espaço total alocado).

Tipo e Assinatura

pub fn ArrayList(comptime T: type) type

O tipo retornado oferece os campos e métodos principais:

  • items — Slice []T dos elementos atuais
  • capacity — Número total de posições alocadas
  • allocator — O alocador usado para gerenciar memória

Funções Principais

Criação e Destruição

// Cria um ArrayList vazio
pub fn init(allocator: Allocator) ArrayList(T)

// Cria com capacidade inicial pré-alocada
pub fn initCapacity(allocator: Allocator, num: usize) Allocator.Error!ArrayList(T)

// Libera toda a memória
pub fn deinit(self: *ArrayList(T)) void

Adição de Elementos

// Adiciona um elemento ao final
pub fn append(self: *ArrayList(T), item: T) Allocator.Error!void

// Adiciona um slice inteiro ao final
pub fn appendSlice(self: *ArrayList(T), items: []const T) Allocator.Error!void

// Insere em posição específica, deslocando elementos
pub fn insert(self: *ArrayList(T), index: usize, item: T) Allocator.Error!void

Remoção de Elementos

// Remove e retorna o último elemento
pub fn pop(self: *ArrayList(T)) T

// Remove e retorna o último, ou null se vazio
pub fn popOrNull(self: *ArrayList(T)) ?T

// Remove elemento em índice específico (preserva ordem)
pub fn orderedRemove(self: *ArrayList(T), index: usize) T

// Remove por troca com último (não preserva ordem, O(1))
pub fn swapRemove(self: *ArrayList(T), index: usize) T

Redimensionamento

// Redimensiona para n elementos
pub fn resize(self: *ArrayList(T), new_len: usize) Allocator.Error!void

// Garante capacidade mínima sem alterar comprimento
pub fn ensureTotalCapacity(self: *ArrayList(T), new_capacity: usize) Allocator.Error!void

// Libera capacidade não utilizada
pub fn shrinkAndFree(self: *ArrayList(T), new_len: usize) void

// Limpa todos os elementos sem liberar memória
pub fn clearRetainingCapacity(self: *ArrayList(T)) void

// Limpa e libera memória
pub fn clearAndFree(self: *ArrayList(T)) void

Exemplo 1: Uso Básico — Construindo uma Lista

const std = @import("std");

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

    // Cria um ArrayList de inteiros
    var lista = std.ArrayList(i32).init(allocator);
    defer lista.deinit();

    // Adiciona elementos
    try lista.append(10);
    try lista.append(20);
    try lista.append(30);
    try lista.appendSlice(&[_]i32{ 40, 50, 60 });

    // Acessa os elementos via slice
    const stdout = std.io.getStdOut().writer();
    try stdout.print("Elementos: ", .{});
    for (lista.items) |item| {
        try stdout.print("{d} ", .{item});
    }
    try stdout.print("\n", .{});
    try stdout.print("Comprimento: {d}, Capacidade: {d}\n", .{
        lista.items.len,
        lista.capacity,
    });

    // Remove o último
    const ultimo = lista.pop();
    try stdout.print("Removido: {d}\n", .{ultimo});
}

Exemplo 2: Filtragem e Transformação

const std = @import("std");

fn filtrarPares(lista: []const i32, allocator: std.mem.Allocator) !std.ArrayList(i32) {
    var resultado = std.ArrayList(i32).init(allocator);
    for (lista) |valor| {
        if (@mod(valor, 2) == 0) {
            try resultado.append(valor);
        }
    }
    return resultado;
}

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

    const dados = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    var pares = try filtrarPares(&dados, allocator);
    defer pares.deinit();

    const stdout = std.io.getStdOut().writer();
    try stdout.print("Números pares: ", .{});
    for (pares.items) |v| {
        try stdout.print("{d} ", .{v});
    }
    try stdout.print("\n", .{}); // Saída: 2 4 6 8 10
}

Exemplo 3: ArrayList como Buffer de Escrita

O ArrayList(u8) pode ser usado como um writer dinâmico para construir strings:

const std = @import("std");

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

    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();

    const writer = buf.writer();
    try writer.print("Nome: {s}, Idade: {d}\n", .{ "Ana", 30 });
    try writer.print("Cidade: {s}\n", .{"São Paulo"});

    // O conteúdo está disponível como string
    const resultado = buf.items;
    const stdout = std.io.getStdOut().writer();
    try stdout.writeAll(resultado);
}

Padrões Comuns

Converter para Slice Owned

Use toOwnedSlice para obter o slice e transferir a propriedade da memória:

var lista = std.ArrayList(u8).init(allocator);
try lista.appendSlice("dados importantes");
const slice = try lista.toOwnedSlice();
defer allocator.free(slice);
// lista foi esvaziada; slice contém os dados

Ordenar Elementos

var lista = std.ArrayList(i32).init(allocator);
// ... adicionar elementos ...
std.mem.sort(i32, lista.items, {}, std.sort.asc(i32));

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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