zig-skills/references/std-buf-map.md

4.4 KiB

std.BufMap / std.BufSet

String-keyed maps and sets that own their strings. Automatically copy and free string keys/values.

When to Use

  • Environment variable storage
  • String-to-string mapping with ownership
  • Set of unique strings with automatic memory management
  • When you don't want to manage string lifetime manually

BufMap (String -> String)

const std = @import("std");

var map = std.BufMap.init(allocator);
defer map.deinit();  // frees all stored strings

// Put copies a new key and value. Replacing an existing key retains the stored
// key allocation and copies only the new value.
try map.put("HOME", "/Users/alice");
try map.put("PATH", "/usr/bin");

// Get
if (map.get("HOME")) |home| {
    std.debug.print("home: {s}\n", .{home});
}

// Replace through the public ownership-aware operation.
try map.put("PATH", "/new/path");

// Remove (frees both key and value)
map.remove("PATH");

// Count
const n = map.count();

BufMap: Move Ownership

// putMove takes ownership instead of copying
const key = try allocator.dupe(u8, "MY_KEY");
const value = try allocator.dupe(u8, "my_value");
map.putMove(key, value) catch |err| {
    allocator.free(key);
    allocator.free(value);
    return err;
};
// On success, the map owns both buffers.

BufMap: Iteration

var it = map.iterator();
while (it.next()) |entry| {
    const key = entry.key_ptr.*;
    const value = entry.value_ptr.*;
    std.debug.print("{s}={s}\n", .{ key, value });
}

BufSet (Set of Strings)

var set = std.BufSet.init(allocator);
defer set.deinit();  // frees all stored strings

// Insert (copies the string)
try set.insert("apple");
try set.insert("banana");
try set.insert("apple");  // no-op, already exists

// Check membership
if (set.contains("apple")) {
    // it's in the set
}

// Remove (frees the string)
set.remove("banana");

// Count
const n = set.count();

BufSet: Iteration

var it = set.iterator();
while (it.next()) |key| {
    std.debug.print("{s}\n", .{key.*});
}

BufSet: Clone

// Create independent copy
var copy = try set.clone();
defer copy.deinit();

// Clone with different allocator
var arena_copy = try set.cloneWithAllocator(arena.allocator());
// No need to deinit if using arena

Complete Example: Environment Variables

const std = @import("std");

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

    var env = std.BufMap.init(alloc);
    defer env.deinit();

    // Set some variables
    try env.put("APP_NAME", "MyApp");
    try env.put("APP_VERSION", "1.0.0");
    try env.put("DEBUG", "true");

    // Update a value
    try env.put("DEBUG", "false");  // replaces, frees old value

    // Print all
    var it = env.iterator();
    while (it.next()) |entry| {
        std.debug.print("{s}={s}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
    }

    // Check and use
    if (env.get("DEBUG")) |debug| {
        if (std.mem.eql(u8, debug, "true")) {
            std.debug.print("Debug mode enabled\n", .{});
        }
    }
}

Complete Example: Unique Words

const std = @import("std");

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

    var words = std.BufSet.init(gpa.allocator());
    defer words.deinit();

    const text = "the quick brown fox jumps over the lazy dog";
    var tokens = std.mem.tokenizeScalar(u8, text, ' ');

    while (tokens.next()) |word| {
        try words.insert(word);  // duplicates automatically ignored
    }

    std.debug.print("Unique words: {}\n", .{words.count()});  // 8

    var it = words.iterator();
    while (it.next()) |word| {
        std.debug.print("  {s}\n", .{word.*});
    }
}

Notes

  • A new insertion copies both strings. Replacing an existing key retains its stored key and replaces the owned value.
  • putMove transfers ownership only on success; on error the caller retains it.
  • A slice from get() is invalidated by replacing/removing its key or deinitializing the map.
  • A pointer from getPtr() is invalidated by resize, removal of that entry, or deinitialization. Replacing the value updates the existing slot.
  • Iteration order is arbitrary; do not rely on insertion order, and do not modify a BufMap or BufSet while an iterator is live.
  • For non-owning string maps, use std.StringHashMap