पाठ 12 / 25

ArrayList, HashMap and Other Containers

Use std containers with explicit allocators.

Growable containers

The standard library provides containers that take allocators. std.ArrayList(T) is a growable array; since Zig 0.15 it is unmanaged by default: it does not store an allocator, so you pass one to methods that may allocate (try list.append(gpa, item), list.deinit(gpa)), and you create it empty with .empty. Its items field is a slice of the current elements; appending can reallocate, so slices and pointers into items may become invalid. toOwnedSlice(gpa) transfers ownership of the buffer to the caller. Hash maps come in several flavours: std.AutoHashMap(K, V) hashes most key types automatically, std.StringHashMap(V) uses string contents as keys, and ArrayHashMap variants preserve insertion order; getOrPut inserts or updates in one lookup. Keys and values are stored by value, so string keys are not copied: the bytes must outlive the map, or you must dupe them. Other containers include std.MultiArrayList (struct-of-arrays layout for cache efficiency), std.PriorityQueue, std.BufSet and std.SinglyLinkedList. Because the standard library is evolving, check the API for your Zig version, especially whether a container is managed or unmanaged.

Revenue by city with ArrayList and StringHashMap

Unmanaged ArrayList (Zig 0.15 style) and getOrPut.

const std = @import("std");

const Order = struct { city: []const u8, total_paise: u64 };

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

    // ArrayList is unmanaged in Zig 0.15: pass the allocator to methods
    var orders: std.ArrayList(Order) = .empty;
    defer orders.deinit(gpa);
    try orders.append(gpa, .{ .city = "Pune", .total_paise = 120_000 });
    try orders.append(gpa, .{ .city = "Delhi", .total_paise = 30_000 });
    try orders.append(gpa, .{ .city = "Pune", .total_paise = 80_000 });

    // StringHashMap stores the allocator; keys must outlive the map (literals do)
    var revenue = std.StringHashMap(u64).init(gpa);
    defer revenue.deinit();

    for (orders.items) |o| {
        const entry = try revenue.getOrPut(o.city);
        if (!entry.found_existing) entry.value_ptr.* = 0;
        entry.value_ptr.* += o.total_paise;
    }

    var it = revenue.iterator();
    while (it.next()) |kv| {
        std.debug.print("{s}: {d}\n", .{ kv.key_ptr.*, kv.value_ptr.* });
    }

    const last = orders.pop();          // ?Order: null when empty
    if (last) |o| std.debug.print("removed last order from {s}\n", .{o.city});
}

Beware of invalidated slices

Holding const first = &list.items[0]; and then appending may move the buffer, leaving first dangling. Store indices instead of pointers when a container can grow.

त्वरित जाँच: In Zig 0.15, how do you append to an unmanaged std.ArrayList?

  • try list.append(allocator, item)
  • list.append(item)
  • list.push(item)
  • ArrayLists cannot grow
Answer

try list.append(allocator, item) — Unmanaged containers take the allocator as an argument to allocating methods.