How do I pass a multidimensional slice to a generic function?

You can also simplify @david_vanderson’s solution. The compiler is able to infer the array types from the result type, so you can use . instead of explicitly naming the type in every line:

    const accounts: []const []const i8 = &.{
        &.{ 1, 2 },
        &.{ 3, 4 },
    };

But keep in mind that with slices this will be stored quite inefficiently. Here is roughly how this would look in memory:

accounts = .{.ptr = 0xabcd, .len = 2}
0xabcd = .{.ptr = 0xbcde, .len = 2, .ptr = 0xcdef, .len = 2}
0xbcde = .{1, 2}
0xcdef = .{3, 4}

That’s why in practice I would recommend to make a struct that makes sure everything is flat in one array. This is what I use when the array length is runtime-known:

pub fn Array2D(comptime T: type) type {
	return struct {
		const Self = @This();
		mem: []T,
		width: u32,
		height: u32,

		pub fn init(allocator: Allocator, width: u32, height: u32) !Self {
			return .{
				.mem = try allocator.alloc(T, width*height),
				.width = width,
				.height = height,
			};
		}

		pub fn deinit(self: Self, allocator: Allocator) void {
			allocator.free(self.mem);
		}

		pub fn get(self: Self, x: usize, y: usize) T {
			std.debug.assert(x < self.width and y < self.height);
			return self.mem[x*self.height + y];
		}

		pub fn getRow(self: Self, x: usize) []T {
			std.debug.assert(x < self.width);
			return self.mem[x*self.height..][0..self.height];
		}

		pub fn set(self: Self, x: usize, y: usize, t: T) void {
			std.debug.assert(x < self.width and y < self.height);
			self.mem[x*self.height + y] = t;
		}

		pub fn ptr(self: Self, x: usize, y: usize) *T {
			std.debug.assert(x < self.width and y < self.height);
			return &self.mem[x*self.height + y];
		}
	};
}

The memory layout of this is much better:

accounts = .{.mem.ptr = 0xabcd, .mem.len = 4, width = 2, .height = 2}
0xabcd = .{1, 2, 3, 4}
3 Likes