Free [*:0]const u8 using std.mem.Allocator.free?

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 *.

Is there something else I should be doing?

The free is correct, because dupeZ uses alloc to allocate memory.

Everything looks good.

I needed to do std.mem.span otherwise got this error:
lib/std/mem.zig:4229:14: error: type '[*:0]const u8' does not support field access

kinda weird span makes sense for [*:0] types?

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).

1 Like

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).

1 Like

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.

Consider using errdefer

{
  const argZ = try self.allocator.dupeZ(u8, arg);
  errdefer self.allocator.free(argZ);
  try self.args.append(argZ);
}

or std.ArrayList(...) s .ensureUnusedCapacity(...) and .appendAssumeCapacity(...)

try self.args.ensureUnusedCapacity(countOfItemsToAdd);
...
const argZ = try self.allocator.dupeZ(u8, arg);
self.args.appendAssumeCapacity(argZ);
2 Likes

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.

1 Like

You may also find these conversions useful: Convert []const u8 to [*:0]const u8 - #2 by dimdin

2 Likes