Because I’m using Zig for writing server code, I’m often facing an issue that I need to construct and struct and have it registered with some mechanism, so I need it to have a stable pointer.
In the most naive form, this can be done as returning pointer to a struct that self destroys itself in deinit. That’s not very idiomatic and it’s surprising, so I don’t like it.
In some cases, I actually need reference counted pointers, so I have them wrapped in my own smartptr container, and that cleaned up the API where I need to use this.
The alternative is to have these structs pre-initialized, and then have functions that take them by pointer and do whatever work needs to be done for post-initialization. This seems idiomatic, but then you have a struct with the init phase split and you need to deal with optionals, etc.
Another approach, which could work in theory, but seems wrong, is to have the structs as undefined and then pass their pointer to init. Thread pool in std work like this. I don’t like the API, but it does work.
What other approaches do you use for cases where you need stable pointer in init?
Instead of a stable reference counted pointer, what about a stable handle, an integer that’s unique across your server to identify your values.
You can store your values in a hashmap then get them using the handle. Something like this:
const Handle = enum(usize) {_};
const Value = struct {};
const Values = struct {
values: std.AutoArrayHashMapUnmanaged(Handle, Value),
ref_count: std.AutoArrayHashMapUnmanaged(Handle, usize),
};
And you can add methods to work with them in a nice way
Andrews’s talk about this has been very eye opening for me
Programming without pointers
It’s close to idiomatic, the idiomatic form would use destroy, not deinit. We don’t have pinned types (yet), so always heap-allocating and destroying the memory using a pair of create and destroy methods is how you can at least document that these structures need to have a stable location throughout their use. It’s also somewhat easier to catch use-after-free if you set the pointer to undefined after destroying the memory it points to.
The better solution will be adding pinning to the type system, which is on the agenda. Things like reference counting will most likely always need to be implemented by hand in Zig, unless someone comes up with a brilliant no-magic way to do it, and I wouldn’t count on that happening.
4 Likes