Null terminated array of strings from C function

The question mark goes on the left of the pointer, not the right. The list itself is not optional, but the elements (the strings) are. If we break it down:

[*:null]               // many-item 'null'-terminated pointer to
        ?              // optional of
         [*:0]const    // many-item '0'-terminated const pointer to
                    u8 // unsigned 8-bit integer

Perhaps the best way to explain complicated pointer types is to use Zig itself:

comptime {
    explainType([*:null]?[*:0]const u8);
}

fn explainType(comptime T: type) void {
    comptime {
        @compileLog("Explaining " ++ @typeName(T));
        var maybe_current: ?type = T;
        while (maybe_current) |current| {
            const info = @typeInfo(current);
            @compileLog(info);
            maybe_current = switch (info) {
                inline .Array,
                .Vector,
                .Pointer,
                .Optional,
                => |parent_info| parent_info.child,
                else => null,
            };
        }
    }
}

Pasting this into any source file and compiling will fail the compilation but print the following useful information to the terminal:

@as(*const [33:0]u8, "Explaining [*:null]?[*:0]const u8")
@as(builtin.Type, .{ .Pointer = .{.size = .Many, .is_const = false, .is_volatile = false, .alignment = 8, .address_space = .generic, .child = ?[*:0]const u8, .is_allowzero = false, .sentinel = null} })
@as(builtin.Type, .{ .Optional = .{.child = [*:0]const u8} })
@as(builtin.Type, .{ .Pointer = .{.size = .Many, .is_const = true, .is_volatile = false, .alignment = 1, .address_space = .generic, .child = u8, .is_allowzero = false, .sentinel = 0} })
@as(builtin.Type, .{ .Int = .{.signedness = .unsigned, .bits = 8} })
9 Likes