I'm trying to alloc 2d arrays of HashMap(u32, u1) in Zig:
fn alloc2d(comptime t: type, m: u32, n: u32, allocator: *Allocator) callconv(.Inline) ![][]t {
const array = try allocator.alloc([]t, m);
for (array) |_, index| {
array[index] = try allocator.alloc(t, n);
}
return array;
}
fn free2d(comptime t: type, array: [][]t, allocator: *Allocator) callconv(.Inline) void {
for (array) |_, index| {
allocator.free(array[index]);
}
allocator.free(array);
}
test "Alloc 2D Array" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = &gpa.allocator;
defer _ = gpa.deinit();
const HashSet = std.AutoHashMap(u32, u1);
var array = try alloc2d(*HashSet, 4, 4, allocator);
defer free2d(*HashSet, array, allocator);
for (array) |_, i| {
for (array[i]) |_, j| {
array[i][j] = &(HashSet.init(allocator));
}
}
defer {
for (array) |_, i| {
for (array[i]) |_, j| {
array[i][j].deinit();
}
}
}
}
However, when I test it, the debugger throw a seg fault.
Can anyone tell me what is happening and how to fix it?
Thanks a lot!
I was having a look over your code and at first glance it seems to do what you're expecting; I wasn't quite sure why you were passing a *HashSet
rather than just a HashSet
to your functions:
...
var array = try alloc2d(HashSet, 4, 4, allocator);
defer free2d(HashSet, array, allocator);
for (array) |_, i| {
for (array[i]) |_, j| {
array[i][j] = HashSet.init(allocator);
}
}
...
In fact, if you do that, everything works as you'd expect.
That said, I couldn't see a reason why your version didn't work, so I had a poke at it, and found that what seems to be happening is that every single item in your array
is being initialised with the same address, i.e. &(HashSet.init(allocator))
is returning the same address every time. I think this is why the deinit
call is segfaulting, the memory is being freed multiple times.
If you manually initialise every element in the array e.g. [0][0] = (HashSet.init(allocator)...etc
everything seems to work. I'm not entirely sure what's going on here, but it might be that there's some kind of compiler optimisation at play, perhaps related to the way generics work. Hopefully someone else will come along with a better answer.
Slightly unrelated, but a neat feature of Zig
, you can iterate over a slice by reference which can sometimes be easier to read:
for (array) |*outer| {
for (outer.*) |*item| {
item.* = <something>
}
}