Array, slices, and function argument

Hi, I am trying to figure out array and slices in Zig, and I am getting stuck when trying to pass “array of array” thing to a function.

I got the following code to work, but I can’t figure out what type to use for the names argument for foo except anytype.

I tried, [][*:0]const u8, [_][*:0]const u8, etc, but Zig insist that it has to be [3][*:0]const u8 or with some known fixed len depending on the caller.

Thanks for any insights on this.

const std = @import("std");

fn foo(names: anytype) void {
    const blah: [names.len][*:0]const u8 = names;

    std.log.info("{d}", .{blah.len});

    std.log.info("{d}", .{blah.len});
    std.log.info("{s}", .{blah[0]});
    std.log.info("{s}", .{blah[1]});
    std.log.info("{s}", .{blah[blah.len - 1]});
}

pub fn main() !void {
    const names = [_][*:0]const u8{ "Hello", "The", "test" };

    foo(names);

    const names2 = [_][*:0]const u8{ "foo", "blah", "ab", "hello" };

    foo(names2);
}

Hi! I’ll try to poke at the problem from a couple different angles but I believe you might want to take a look at What's a String Literal in Zig? - Zig NEWS (also check out the video at the end of the article).

In your code, foo(names) is passing to foo the entire array as a value. names, to be precise, is an array of null-terminated pointers. Arrays encode their length in the type and as such the only way to pass arrays of different length to a function, is by using generics, which is what happens with anytype.

Normally, and especially if you plan to have a function operate on arrays of varying length, you want to pass around a slice, which is a pointer + length. To do that you need to change the signature of foo to make it accept a slice and then pass to foo a pointer to your arrays (Zig will automatically coerce the pointer to a slice). This is the signature:

fn foo(names: []const [*:0]const u8) void

// at the callsite
foo(&names);

At this point the only problem remaining is the first line of foo, where you create blah. I imagine that it’s just a thing that you did while trying to figure out how to make everything compile but I just want to point out that this line is conceptually incompatible with slices. Since slices have a length that’s known at runtime, the compiler can’t create a stack variable of a size that is not known at compile time and in fact if you try to compile the code without deleting that line you will get an error that will tell you the same.

If you wanted to do something similar with slices, you would need heap allocation.

Hopefully the blog post + video should help you get some practice with slices, pointers and arrays. If you still have any question afterwards feel free to ask :^)

1 Like

Thank you. That makes sense.

I actually tried with &names, except I did names: [][*:0]const u8, missing the const, and Zig gave the following error, which I should’ve taken the time to properly understand.

./array1.zig:18:10: error: expected type '[][*:0]const u8', found '*const [3][*:0]const u8'
    foo(&names);

Yeah that trips up everybody in the beginning, and the error doesn’t help too much if you don’t know all the coercion rules.