Issue with passing 2D bool array as function parameter

I am learn learning recursion and am trying to do a practice problem, but I’m having an issue with passing a 2d bool array as a function parameter.

The error:

path_finding.zig:18:78: error: expected type [ ][ ]bool’, found ‘*[3][7]bool’
const path_found:bool = walk(&maze, ‘#’, current_location, end_location, &visited_coordinate, &path, path_index);
^~~~~~~~~~~~~~~~~~~
path_finding.zig:18:78: note: pointer type child ‘[7]bool’ cannot cast into pointer type child ‘[ ]bool’
path_finding.zig:24:81: note: parameter type declared here
fn walk(maze:[ ]const[ ]const u8, wall:u8, cur_point:Point, end_point:Point, seen:[ ][ ]bool, path:[
]Point, path_index:u8) bool {

what I don’t understand is why passing visited_coordinate as a parameter causes this issue while maze passes just fine. When I remove & from the function call, I get a “variable never mutated” error. What do I need to do to make this work?

const std = @import("std");
const Point = struct {
    x:u8,
    y:u8,
};

pub fn main() void {
    const maze = [_][]const u8{"#####E#",
                               "#     #",
                               "#S#####",
    };
    var visited_coordinate:[3][7]bool = .{.{false} ** 7} ** 3;
    const current_location:Point = .{.x = 1, .y = 2};
    const end_location:Point = .{.x = 5, .y = 0};
    var path:[7]Point = undefined;
    const path_index = 0;      
    const path_found:bool = walk(&maze, '#', current_location, end_location, &visited_coordinate, &path, path_index);
    if (path_found == true) {
        std.debug.print("{any}\n", .{path});
    }
}

fn walk(maze:[]const[]const u8, wall:u8, cur_point:Point, end_point:Point, seen:[][]bool, path:[]Point, path_index:u8) bool {
    _ = maze;
    _ = wall;
    _ = cur_point;
    _ = end_point;
    _ = seen;
    _ = path;
    _ = path_index;
    //code to be written
} 

[ ][ ]bool = slice of slices of bools
*[3][7]bool = slice of arrays of bools

your problem is that you have a slice of arrays instead of a slice of slices. so if you put & before all the arrays in visited_coordinate it turns them into slices.

var visited_coordinate: [3][]bool = .{@constCast(&[_]bool{false} ** 7)} ** 3;

the @constCast is to convert &[_]bool{false} ** 7 from type *const [7]bool to []bool (which is basically *[7]bool).

if you don’t need to mutate seen in the walk function you can further simplify to

const visited_coordinate: [3][]const bool = .{&[_]bool{false} ** 7} ** 3;

and

seen: []const []const bool

edit:
maze doesn’t error because it is already a slice of slices. the type of "#####E#" is *const [7:0] u8 (which means const slice of u8’s which is terminated by a 0 byte), so it can implicitly convert itself to []const u8 because it is a pointer to a const array. and maze is type const [3][]const u8 which when taking a pointer to it converts into []const []const u8 which is the type signature of the maze parameter in the walk function.

When I tried doing your first recommendation it does compile, but when I try to mutate the value it leads to a segfault, but printing the values does not.

//change put in main:
var visited_coordinate: [3][]bool = .{@constCast(&[_]bool{false} ** 7)} ** 3;

//in walk function
std.debug.print("{any}\n", .{seen});
std.debug.print("{any}\n", .{seen[0]});
std.debug.print("{any}\n", .{seen[0][0]});
seen[0][0] = true;

Output:

{ { false, false, false, false, false, false, false }, { false, false, false, false, false, false, false }, { false, false, false, false, false, false, false } }
{ false, false, false, false, false, false, false }
false
Segmentation fault at address 0x1003d51
path_finding.zig:34:12: 0x1037ab8 in walk (path_finding)
seen[0][0] = true;
^

So I am not sure why it is treating it like I am touching out of bounds memory. Is there an alternative way of passing my original array this array to the function, or is there something I am doing that is illegal that I am not seeing?

Using @constCast is a bad advice, and leads to problems just like this. It only exists to allow calling libraries that haven’t used const-correctness in their API, and you will almost never encounter these.

Also I think it would be helpful to further explain the difference between an array and a slice:
An array is a flat container, there is no pointer, if you declare x: [7]bool then there will be 7 bools directly next to each other in memory in that location. Similarly with x: [3][7]bool there will 21 bools directly next to each other in memory.
A slice on the other hand is an indirect data type. You can think of it as
[]T == struct{ptr: [*]T, len: usize}
So the memory layout of a [][]bool is completely different to the memory layout of a [3][7]bool, despite the similarities of their syntax.

Now to solve your problem, I’d suggest you check out this post, which asked the same question: How do I pass a multidimensional slice to a generic function?

3 Likes

Thanks! I opted for just throwing the 2D array into a struct and it works just fine now. I will keep in mind generic functions in the future for other applications.

2 Likes