Dynamically allocate ArrayList for use in HashMap

Hi there!

I’m trying to make a HashMap with values that are ArrayLists, though the array lists have to be initialized dynamically in a loop.
I can’t really seem to get it to work, I think I don’t have a clear model of what is stored where yet in Zig and after trying for a while now I feel it is time to ask :sweat_smile:

Test 1:

const std = @import("std");

pub fn main() !void {
    var allocator_type = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = allocator_type.allocator();

    var data = std.AutoHashMap(usize, std.ArrayList(usize)).init(allocator);
    defer {
        var it = data.valueIterator();
        while (it.next()) |val| {
            val.deinit();
        }
        data.deinit();
    }

    for (0..10) |i| {
        const idx = i % 2;
        if (data.get(idx)) |list| {
            try list.append(i);
        } else {
            var list = std.ArrayList(usize).init(allocator);
            try list.append(i);
            try data.put(idx, list);
        }
    }

    var it = data.iterator();
    while (it.next()) |entry| {
        std.debug.print("{d}: {any}\n", .{entry.key_ptr.*, entry.value_ptr.*.items});
    }
}

This was the first thing I thought to do, and I get error: expected type '*array_list.ArrayListAligned(usize,null)', found '*const array_list.ArrayListAligned(usize,null)' from the call to append. I’m not sure if I can easily make this work, but I didn’t manage at least.

Test 2:

const std = @import("std");

pub fn main() !void {
    var allocator_type = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = allocator_type.allocator();

    var data = std.AutoHashMap(usize, *std.ArrayList(usize)).init(allocator);
    defer {
        var it = data.valueIterator();
        while (it.next()) |val| {
            val.*.deinit();
            allocator.destroy(val);
        }
        data.deinit();
    }

    for (0..10) |i| {
        const idx = i % 2;
        if (data.get(idx)) |*list| {
            try list.*.append(i);
        } else {
            const ptr = try allocator.create(std.ArrayList(usize));
            ptr.* = std.ArrayList(usize).init(allocator);
            try ptr.*.append(i);
            try data.put(idx, ptr);
        }
    }

    var it = data.iterator();
    while (it.next()) |entry| {
        std.debug.print("{d}: {any}\n", .{entry.key_ptr.*, entry.value_ptr.*.items});
    }
}

Trying to ensure that I have the pointers I want I tried this, and now it actually kind of works. I get the printout I want first, but after it crashes on the deallocation. But it also feels quite convoluted, and wanted to know if there was an easier way and how I should do the deallocation correctly if this is the way.

Maybe this recent discussion helps: Problem with HashMaps - #8 by SpinTensor

1 Like

Yeah, my first approach with getPtr instead of get seems to do it. Thanks!