std.HashMap requires you to specify your own context - a context is a namespace that contains a hash and eql function, for computing hashes and hash equality.
This is great for custom implementations, but for people who don’t need it, std.AutoHashMap exists which uses a default context.
std.ArrayHashMap is a hash map which guarantees that the keys will all be stored next to each other in memory, (as will the values), so you know everything will be in the hash map in the same order you put it in there, and you can call .keys() and .values() to get a slice of the map’s keys and values, which is nice and convenient compared to std.HashMap where you’re forced to use an iterator type.
std.AutoArrayHashMap is just the same thing as std.AutoHashMap but for array hash maps.
In your case, you’d probably do std.AutoHashMap(u32, []const u8) or std.AutoArrayHashMap(u32, []const u8).
You don’t need to allocate the keys - the hash map will do that for you - but if your values are buffers, and they’re allocated on the heap, you do need to free them; deinitting the hash map won’t do that on its own.
In light of that, your deinit routine might look like this:
/// deinit shouldn't be able to fail
pub fn deinit(self: *MyHash) void {
// If you used an AutoArrayHashMap, you can use the values function
// to get a []const []const u8 slice of all your slices.
// You can then loop over this slice and use your allocator to free each buffer.
for(self.map.values()) |buffer| {
self.allocator.free(buffer);
}
self.map.deinit();
}
If you used a std.AutoHashMap instead of a std.AutoArrayHashMap, it might look like this instead:
pub fn deinit(self: *MyHash) void {
var it = self.map.iterator();
while(it.next()) |entry| {
// entry.value_ptr is a pointer to a []const u8, so we need to
// dereference it to properly free it.
self.allocator.free(entry.value_ptr.*);
}
self.map.deinit();
}
I also feel compelled to point out that your init function is kind of unnecessary.
In the standard library, it’s considered standard for structs to have a pub const init: @This() = .{}; declaration or similar, which is a pre-instantiated instance of the struct with all the desired initial values.
That way, users can instantiate the struct using a pattern like this:
pub var arrl: std.ArrayList(u32) = .empty;
This communicates the user’s intent to create an ArrayList with no items and a starting capacity of 0 items.
Since your init function isn’t actually using any inputs other than the struct itself, it could easily be changed to a declaration of this sort, like so:
pub const init: myhash = .{
.allocator = std.heap.smp_allocator,
.map = .init(std.heap.smp_allocator),
};