I'm trying to learn Zig v0.13.0, and I'm playing around with small programs. While playing, I tried to create a "global allocator", which is more convenient for now.
So at first, I did something like that:
const std = @import("std");
const key_t = u32;
const value_t = u128;
const computed_map_t = std.AutoHashMap(key_t, value_t);
var default_gpa: std.heap.GeneralPurposeAllocator(.{}){};
const default_allocator = default_gpa.allocator();
pub fn fibonacci(value: key_t) value_t {
const _state = struct {
var previously_computed = computed_map_t.init(default_allocator);
};
// ...
}
pub fn main() !void {
const value = 5;
const result = fibonacci(value);
std.debug.print("fibonacci({d}) = {d}\n", .{ value, result });
if (.leak == default_gpa.deinit()) {
@panic("GPA leaked !");
}
}
I then changed it to:
const std = @import("std");
const key_t = u32;
const value_t = u128;
const computed_map_t = std.AutoHashMap(key_t, value_t);
// moved initialization to `main`
var default_gpa: std.heap.GeneralPurposeAllocator(.{}) = undefined;
const default_allocator = default_gpa.allocator();
pub fn fibonacci(value: key_t) value_t {
const _state = struct {
var previously_computed = computed_map_t.init(default_allocator);
};
// ...
}
pub fn main() !void {
default_gpa = @TypeOf(default_gpa){};
defer {
if (.leak == default_gpa.deinit()) {
@panic("GPA leaked !");
}
}
const value = 5;
const result = fibonacci(value);
std.debug.print("fibonacci({d}) = {d}\n", .{ value, result });
}
I though it would fail, because const default_allocator = default_gpa.allocator()
is now initialized with undefined memory, but to my surprise, it seems to work.
From what I can read about Container level variables,
The initialization value of container level variables is implicitly comptime. If a container level variable is const then its value is comptime-known, otherwise it is runtime-known.
First question: how can the initialization value be implicitly comptime if it's only known at runtime in the case of a global variable using var
? I assumed that "comptime initalized" and "comptime-known" were mostly equivalent (to initialize a variable at comptime, you need to know its value at comptime), but this seems to say otherwise. I couldn't find an exact definition for those terms, so I don't fully get the difference.
Then, following this rule, const default_allocator
should be comptime initialized and its value comptime-known (computed during compilation?), but I doesn't seem to be the case.
Either it's the case, it's undefined behavior, and I'm lucky it works for now, or there is something I'm not aware of, that makes the whole thing work.
I have a couple of ideas to explain why this works, but I couldn't find any documentation to back them up:
const
global variables that don't depend on runtime-known variables are themselves comptime-known. am I right on this?_state
struct is also initalized the first time the function is called, which works in my case (the allocator is only used to initialize the HashMap inside the function, so it's called after the allocator is initialized).I didn't find any documentation about this (or missed/misinterpreted something), if somebody has an explanantion and possibly a link to some documentation, I would appreciate it a lot.
It's because the call to allocator is simply returning an vtable with function pointers in a struct.
The only catch is that it captures the pointer to default_gpa so it can reference it later.
const default_allocator = default_gpa.allocator();
This call succeeds even with default_gpa uninitialized, as the pointer itself is fine.
pub fn allocator(self: *Self) Allocator {
return .{
.ptr = self,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.free = free,
},
};
}
The memory ptr is pointing to is filled with 0xAAAA(undefined) values. But that will only be until your first line of main where it gets initialized.
Now if you were to try to call alloc() on the allocator interface before the default_gpa gets initialized, then the moment it goes to deref ptr and access the memory there, bad things would likely happen.
Though you should try it out and see what happens!