Coerce slice to array pointer

How do I convert a slice []const u8 to an array pointer *const [2]u8?

const std = @import("std");

test {
    const arr: [5]u8 = .{ 0, 1, 2, 3, 4 };
    const slice: []const u8 = arr[2..4];
    try testArrPtr(slice);
}

fn testArrPtr(arr_ptr: *const [2]u8) void {
    try std.testing.expect(arr_ptr[0] == 2);
    try std.testing.expect(arr_ptr[1] == 3);
}
test.zig:8:20: error: expected type '*const [2]u8', found '[]const u8'
    try testArrPtr(slice);
                   ^~~~~
test.zig:11:24: note: parameter type declared here
fn testArrPtr(arr_ptr: *const [2]u8) void {
                       ^~~~~~~~~~~~

I guess we are slicing twice my friends…

const std = @import("std");

test {
    const arr: [5]u8 = .{ 0, 1, 2, 3, 4 };
    const slice: []const u8 = arr[2..4];
    try testArrPtr(slice[0..2]);
}

fn testArrPtr(arr_ptr: *const [2]u8) !void {
    try std.testing.expect(arr_ptr[0] == 2);
    try std.testing.expect(arr_ptr[1] == 3);
}
$ zig test test.zig 
All 1 tests passed.

It’s interesting that the arr[2..4] does not result in an array pointer, even though both ends of the range are comptime-known. Does that mean that Zig special-cases specifically on [0..n]? Edit: see below reply

With that said, it seems to be somewhat common to represent “at index i, take a slice of n elements” like so in Zig:

original_slice_or_array[i..][0..n]

Oh, no, actually, your problem is different!

Slices do not coerce to array pointers, it is the other way around. When the slice size is comptime-known, like in arr[2..4], this results in a pointer to array, which then may transparently coerce to a slice. Since you are explicitly specifying that slice is []const u8, you cannot then pass it to testArrPtr. If you remove the type (and add the missing ! in testArrPtr’s return type), the original code compiles:

const std = @import("std");

test {
    const arr: [5]u8 = .{ 0, 1, 2, 3, 4 };
    const slice = arr[2..4];
    try testArrPtr(slice);
}

fn testArrPtr(arr_ptr: *const [2]u8) !void {
    try std.testing.expect(arr_ptr[0] == 2);
    try std.testing.expect(arr_ptr[1] == 3);
}
3 Likes
    // If you slice with comptime-known start and end positions, the result is
    // a pointer to an array, rather than a slice.
    const array_ptr = array[0..array.len];
    try expect(@TypeOf(array_ptr) == *[array.len]i32);

    // You can perform a slice-by-length by slicing twice. This allows the compiler
    // to perform some optimisations like recognising a comptime-known length when
    // the start position is only known at runtime.
    var runtime_start: usize = 1;
    _ = &runtime_start;
    const length = 2;
    const array_ptr_len = array[runtime_start..][0..length];
    try expect(@TypeOf(array_ptr_len) == *[length]i32);
4 Likes