std.StringHashMap em Zig — Referência e Exemplos

std.StringHashMap — HashMap Especializado para Strings

O std.StringHashMap é uma especialização do HashMap otimizada para chaves do tipo []const u8 (strings). Ele oferece a mesma interface do HashMap mas já vem configurado com a função de hash e comparação corretas para slices de bytes, eliminando a necessidade de implementar um contexto customizado.

Visão Geral

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

O StringHashMap é essencialmente um alias conveniente:

pub fn StringHashMap(comptime V: type) type {
    return HashMap([]const u8, V, std.hash_map.StringContext, ...);
}

Como strings em Zig são []const u8, este é o tipo mais natural para construir mapas chave-valor onde as chaves são texto.

Funções Principais

Criação e Destruição

// Cria um mapa vazio
pub fn init(allocator: Allocator) StringHashMap(V)

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

Inserção e Acesso

// Insere ou atualiza um par chave-valor
pub fn put(self: *Self, key: []const u8, value: V) Allocator.Error!void

// Busca valor pela chave
pub fn get(self: Self, key: []const u8) ?V

// Busca ponteiro para o valor (permite modificação)
pub fn getPtr(self: *Self, key: []const u8) ?*V

// Insere se não existe
pub fn getOrPut(self: *Self, key: []const u8) Allocator.Error!GetOrPutResult

// Verifica se contém a chave
pub fn contains(self: Self, key: []const u8) bool

Remoção e Iteração

// Remove e retorna o valor
pub fn fetchRemove(self: *Self, key: []const u8) ?KV

// Remove a chave
pub fn remove(self: *Self, key: []const u8) bool

// Iterador sobre todas as entradas
pub fn iterator(self: Self) Iterator

Exemplo 1: Tabela de Variáveis de Ambiente

const std = @import("std");

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

    var env = std.StringHashMap([]const u8).init(allocator);
    defer env.deinit();

    // Simula variáveis de ambiente
    try env.put("HOME", "/home/usuario");
    try env.put("PATH", "/usr/bin:/usr/local/bin");
    try env.put("LANG", "pt_BR.UTF-8");
    try env.put("EDITOR", "vim");
    try env.put("SHELL", "/bin/bash");

    const stdout = std.io.getStdOut().writer();

    // Busca individual
    if (env.get("HOME")) |home| {
        try stdout.print("Diretório home: {s}\n", .{home});
    }

    if (env.get("DISPLAY")) |_| {
        try stdout.writeAll("Display configurado\n");
    } else {
        try stdout.writeAll("Display não configurado\n");
    }

    // Lista todas as variáveis
    try stdout.writeAll("\nVariáveis de ambiente:\n");
    var it = env.iterator();
    while (it.next()) |entry| {
        try stdout.print("  {s}={s}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
    }
}

Exemplo 2: Parser de Configuração Simples

const std = @import("std");

const ConfigError = error{FormatoInvalido};

fn parseConfig(
    texto: []const u8,
    allocator: std.mem.Allocator,
) !std.StringHashMap([]const u8) {
    var config = std.StringHashMap([]const u8).init(allocator);
    errdefer config.deinit();

    var linhas = std.mem.splitSequence(u8, texto, "\n");
    while (linhas.next()) |linha| {
        // Ignora linhas vazias e comentários
        const trimmed = std.mem.trim(u8, linha, " \t");
        if (trimmed.len == 0 or trimmed[0] == '#') continue;

        // Divide em chave=valor
        if (std.mem.indexOf(u8, trimmed, "=")) |sep| {
            const chave = std.mem.trim(u8, trimmed[0..sep], " ");
            const valor = std.mem.trim(u8, trimmed[sep + 1 ..], " ");
            try config.put(chave, valor);
        }
    }

    return config;
}

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

    const texto_config =
        \\# Configuração do servidor
        \\host = localhost
        \\porta = 8080
        \\max_conexoes = 100
        \\
        \\# Banco de dados
        \\db_host = 127.0.0.1
        \\db_nome = app_prod
    ;

    var config = try parseConfig(texto_config, allocator);
    defer config.deinit();

    const stdout = std.io.getStdOut().writer();
    try stdout.writeAll("Configuração carregada:\n");
    var it = config.iterator();
    while (it.next()) |entry| {
        try stdout.print("  {s} = {s}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
    }

    // Acesso direto
    const porta = config.get("porta") orelse "3000";
    try stdout.print("\nServidor rodará na porta: {s}\n", .{porta});
}

Exemplo 3: Registro de Comandos

const std = @import("std");

const ComandoFn = *const fn ([]const u8) void;

fn cmdAjuda(args: []const u8) void {
    _ = args;
    std.debug.print("Comandos disponíveis: ajuda, versao, sair\n", .{});
}

fn cmdVersao(args: []const u8) void {
    _ = args;
    std.debug.print("Versão 1.0.0\n", .{});
}

fn cmdSair(args: []const u8) void {
    _ = args;
    std.debug.print("Saindo...\n", .{});
}

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

    var comandos = std.StringHashMap(ComandoFn).init(allocator);
    defer comandos.deinit();

    try comandos.put("ajuda", cmdAjuda);
    try comandos.put("versao", cmdVersao);
    try comandos.put("sair", cmdSair);

    // Simula execução de comandos
    const entradas = [_][]const u8{ "ajuda", "versao", "desconhecido" };

    for (entradas) |entrada| {
        if (comandos.get(entrada)) |cmd| {
            cmd("");
        } else {
            std.debug.print("Comando desconhecido: {s}\n", .{entrada});
        }
    }
}

StringHashMap vs AutoHashMap com []const u8

O StringHashMap é preferível ao AutoHashMap([]const u8, V) porque:

  • Comparação por conteúdo: StringHashMap compara o conteúdo das strings, enquanto AutoHashMap compararia os ponteiros e comprimentos do slice.
  • Hash do conteúdo: O hash é calculado sobre os bytes da string, não sobre o fat pointer.
  • Conveniência: Não requer configuração manual do contexto de hashing.

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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