var args = std.ArrayList([*:0]const u8).init(allocator);
defer {
for (args.items) |arg| {
allocator.free(arg);
}
args.deinit();
}
// everything is appended after copying using `std.mem.Allocator.dupeZ`
// `arg` should be a []const u8 type
const argZ = try self.allocator.dupeZ(u8, arg);
try self.args.append(argZ);
Context? Well, obviously I am trying to pass the “args” to a C function, for my case the function accepts const char *const *.
You can use span to convert a zero terminated pointer [*:0] to a zero terminated slice [:0] (span discovers the length of the string by locating the zero terminator).
Zigs allocators work with an explicit size (c allocators store meta data about the size internally and thus their call to free doesn’t require the size).
The benefit of this is that allocators can be implemented that don’t have to store the size internally in meta-data, the downside is that the user has to make sure to pass the exact same sized data to the free call that was used to allocate it.
[*:0]const u8 is a multi item pointer that is sentinel terminated, it has a specific size, but that size isn’t stored as a length directly, by using std.mem.span you scan over the memory to find the length and then that is returned as a [:0]const u8 which is a sentinel terminated slice which also stores the length explicitly.
The allocator wants the slice as argument to the free call because it has an explicit size and this makes the interface easier (everything has an explicit size).
Not directly related to your question. Depending on your use of self.args the second line can fail. Then you might be leaking memory depending on the allocator used.
This discussion helps me too. I’m learning how to pass data in 0 terminated strings, i.e. like-C style, to call Zig functions from Python. I was getting first the string length using std.mem.len, to pass in indexed form the pointer to be deallocated. Now, I’m using std.mem.span in one step. Thanks.