I don't understand this compiler error (slicing with sentinels)

This is an example that triggers the error (Yes, I know if it actually executed it would crash)

export fn main(count: usize) void {
   var myBuffer: [:0]u8 = undefined;
   var myBuffer2: [:0]u8 = undefined;
   myBuffer = myBuffer2[0..count];
}

The error:

example.zig:4:24: error: expected type '[:0]u8', found '[]u8'
example.zig:4:24: note: destination pointer requires '0' sentinel

Both myBuffer and myBuffer2 are [:0]u8, so why does the compiler say it isn’t? Is it because slicing myBuffer2 causes it to drop the sentinel part from the type? If that’s true then how can I take a slice of a [:0]u8 and keep the sentinel component of the type?

In case it matters, my actual code looks more like this:

const myNewBuffer = allocator.allocSentinel(u8, capacity, 0);
// capacity is guaranteed to be > len
mySubsetSlice = myNewBuffer[0..len];
mySubsetSlice[len] = 0;

I want to allocate a buffer, then have a slice that represents the subset of the buffer that contains content, but also keep a null sentinel at the end of the content to make it easier to work with some C libraries I’m using.

Sentinel terminated slicing is documented in the language reference.

export fn main(count: usize) void {
   var myBuffer: [:0]u8 = undefined;
   var myBuffer2: [:0]u8 = undefined;
   myBuffer = myBuffer2[0..count :0];
}

Note that this specific example will result in a runtime error in safe build modes because you haven’t actually written a zero sentinel at myBuffer2[count] (well technically both slices are just pointing to random memory since they were declared undefined).

3 Likes