I imagine if I had more experience in Zig, none of that would be a problem, and its not aiming to be a beginner-friendly language anyway, but that was just my experience of it so far
I imagine if I had more experience in Zig, none of that would be a problem, and its not aiming to be a beginner-friendly language anyway, but that was just my experience of it so far
Zig hashmaps _are_ quite a bit more cumbersome than, eg, in Rust --- you need to decided whether you want the allocator to be bundled with the hasmap, and its also up to the user of the hash map to provide equality and hash code (and, of course, there's manual defer instead of RAII).
However, this flexibility and verbosity comes with a super-power --- you can pass an allocator to a hash map at creation time, and then _not_ pass an allocator when you actually use the hash map. This is huge. This means that we get compile-time guarantee that we follow rule 3 of NASA's 10 rules
3. Do not use dynamic memory allocation after initialization.
And we _still_ can enjoy using a hashmap from the standard library! No other language I know has an equivalent tool.In Zig, the map could be initialized and sized at runtime, but you still can enforce, statically, that it doesn't do any allocations after that.
In both nightly Rust and Zig, heapless version can be expressed by passing a fixed-buffer-backed allocator to the standard hash map.
https://github.com/ziglang/zig/blob/0dffab7356685c7643aa6e3c...
https://github.com/ziglang/zig/blob/0dffab7356685c7643aa6e3c...
One of them requires passing an allocator (and can allocate), the other doesn’t have an allocator argument (and thus can’t allocate). If you only pass allocator to `init` method of your application, and don’t store it anywhere, only init will be able to call allocating methods, the rest of the app will be allocation free, by construction.
I can see that the difference between the two is in self.growIfNeeded() call, https://github.com/ziglang/zig/blob/0dffab7356685c7643aa6e3c..., which the one that doesn't allocate really doesn't have. Does it assume that the predefined capacity will not be reached?
Yes, the function name says exactly that as well, though admittedly what this actually means and what consequences it might have if the assumption is false are probably fairly opaque to a novice user.
> how does it handle the case where you don't have enough capacity to insert the new (k,v) pair?
Breaking the invariant results in safety-checked undefined behavior, that's what the "asserts" in the doc comment signifies[0]. Basically, if something goes awry at runtime you'll get a crash along with a nice error trace in Debug/ReleaseSafe mode or with the appropriate @setRuntimeSafety call.
If instead you'd like to have errors that you can handle, you could use your real allocator where you expect to actually use it and pass a failing allocator[1] everywhere else (but that's sort of abusing the API IMO, I don't know if I'd actually recommend you do this).
[0] https://ziglang.org/documentation/master/#Doc-Comment-Guidan...
[1] https://github.com/ziglang/zig/blob/0dffab7356685c7643aa6e3c...
This also means that there is nothing novel about this approach that cannot be achieved in other programming languages as one of the parent comments claimed.
This is simply a hashmap with pre-allocated pool of memory.
I completely agree: this has been my experience as well.
Without getting into the weeds of why (happy to do so, just want to keep this readable), basically I needed to define and populate a hashmap in a new script and then import it into my main script, which to my mind left me with two options:
* Define and initialise it at the same time (my preferred method) as a constant. I don't have much to say on this as iirc, I had no luck with it at all, never even got close.
* Define it in a (public) function, add each field in with "put" and then return the hashmap. I tried with std.AutoHashMap and various other things, but to what I could work out there was no type of hashmap, so it wouldn't accept my return type.
const mymap = {1: "hello", 2: "world"};
But the only thing I could find any answers for was something more like: const mymap; mymap.put(1, "hello"); mymap.put(2, "world"); fn buildMap(allocator: std.mem.Allocator) !std.AutoHashMap(u64, u64) {
var result = std.AutoHashMap(u64, u64).init(allocator);
errdefer result.deinit();
try result.put(10, 100);
try result.put(20, 200);
return result;
}
Your #1 option should be possible once Zig has comptime allocators -- it's on the roadmap, but not possible yet iirc.Cheers for letting me know though!