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:
StringHashMapcompara o conteúdo das strings, enquantoAutoHashMapcompararia 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
- std.HashMap — HashMap genérico
- std.ArrayHashMap — HashMap com ordem de inserção
- std.StringArrayHashMap — ArrayHashMap para strings
- std.mem — Operações de memória e strings
- std.fmt — Formatação de strings