# How to create a multidimensional mutable slice?

Hello there,

I am currently working through the Project Euler and I am currently about to finish the 18th problem. I will not post my solution (as I don’t want to spoil anything for other people), but I got a question.

In that problem, we have a triangle. I decided to simulate that using a two-dimensional slice:

``````    const triangle: []const []const u8 = &[_][]const u8{
&[_]u8{75},
&[_]u8{ 95, 64 },
&[_]u8{ 17, 47, 82 },
&[_]u8{ 18, 35, 87, 10 },
&[_]u8{ 20, 4, 82, 47, 65 },
&[_]u8{ 19, 1, 23, 75, 3, 34 },
&[_]u8{ 88, 2, 77, 73, 7, 63, 67 },
&[_]u8{ 99, 65, 4, 28, 6, 16, 70, 92 },
&[_]u8{ 41, 41, 26, 56, 83, 40, 80, 70, 33 },
&[_]u8{ 41, 48, 72, 33, 47, 32, 37, 16, 94, 29 },
&[_]u8{ 53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14 },
&[_]u8{ 70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57 },
&[_]u8{ 91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48 },
&[_]u8{ 63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31 },
&[_]u8{ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23 },
};
``````

However, I want to mutate data in that slice (in fact, my solution requires that). So I asked myself how I could do mutable multidimensional slices. I tried removing the const in the type, the Zig compiler complained about `&[_]u8{}` being const. Then `@constCast`. Threw a bus error.

How to do it correctly?

1 Like

There is probably a better way to do it properly but here’s my attempt

``````const std = @import("std");

pub fn main() !void {
const allocator = std.heap.page_allocator;

var triangle = std.ArrayList([]u8).init(allocator);
defer triangle.deinit();

var items = &[_]u8{
75, 95, 64, 17, 47, 82, 18, 35, 87, 10, 20, 4,  82,
47, 65, 19, 1,  23, 75, 3,  34, 88, 2,  77, 73, 7,
63, 67, 99, 65, 4,  28, 6,  16, 70, 92, 41, 41, 26,
56, 83, 40, 80, 70, 33, 41, 48, 72, 33, 47, 32, 37,
16, 94, 29, 53, 71, 44, 65, 25, 43, 91, 52, 97, 51,
14, 70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57,
91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48,
91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48,
63, 66, 4,  68, 89, 53, 67, 30, 73, 16, 69, 87, 40,
31, 4,  62, 98, 27, 23, 9,  70, 98, 73, 93, 38, 53,
60, 4,  23,
};

try triangle.append(try allocator.dupe(u8, items[0..1]));
try triangle.append(try allocator.dupe(u8, items[1..3]));
try triangle.append(try allocator.dupe(u8, items[3..6]));
try triangle.append(try allocator.dupe(u8, items[6..10]));
try triangle.append(try allocator.dupe(u8, items[10..15]));
try triangle.append(try allocator.dupe(u8, items[15..21]));
try triangle.append(try allocator.dupe(u8, items[21..28]));
try triangle.append(try allocator.dupe(u8, items[28..36]));
try triangle.append(try allocator.dupe(u8, items[36..45]));
try triangle.append(try allocator.dupe(u8, items[45..55]));
try triangle.append(try allocator.dupe(u8, items[55..66]));
try triangle.append(try allocator.dupe(u8, items[66..78]));
try triangle.append(try allocator.dupe(u8, items[78..91]));
try triangle.append(try allocator.dupe(u8, items[91..105]));
try triangle.append(try allocator.dupe(u8, items[105..120]));

// Convert ArrayList to a slice
var mutable_triangle = try triangle.toOwnedSlice();

// Mutate some values in the mutable triangle
mutable_triangle[0][0] = 76; // Change 75 to 76
mutable_triangle[1][0] = 96; // Change 95 to 96

// Print the mutated triangle
for (mutable_triangle) |row| {
std.debug.print("{any}\n", .{row});
}
}
``````

Here is one possibility:

``````const std = @import("std");

pub fn main() !void {
const args = .{
.{75},
.{ 95, 64 },
.{ 17, 47, 82 },
.{ 18, 35, 87, 10 },
.{ 20, 4, 82, 47, 65 },
.{ 19, 1, 23, 75, 3, 34 },
.{ 88, 2, 77, 73, 7, 63, 67 },
.{ 99, 65, 4, 28, 6, 16, 70, 92 },
.{ 41, 41, 26, 56, 83, 40, 80, 70, 33 },
.{ 41, 48, 72, 33, 47, 32, 37, 16, 94, 29 },
.{ 53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14 },
.{ 70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57 },
.{ 91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48 },
.{ 63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31 },
.{ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23 },
};

comptime var length: usize = 0;
inline for (args) |a| length += a.len;

var backing_array: [length]u8 = undefined;
var triangle: [args.len][]u8 = undefined;

comptime var start = 0;
inline for (args, 0..) |a, i| {
const end = start + a.len;
triangle[i] = backing_array[start..end];
inline for (backing_array[start..end], a) |*dest, val| dest.* = val;
start = end;
}

for (triangle) |t| {
std.debug.print("{any}\n", .{t});
}

triangle[2][1] = 179;

for (triangle) |t| {
std.debug.print("{any}\n", .{t});
}
}
``````

It uses `args` as a description for what to create, two vars `backing_array` and `triangle` for the runtime mutable storage, the former holds the elements in a flat array, the latter holds the slices in an array.

It then uses some comptime code to initialize the arrays and copy over the values from args to the `backing_array` and create the slices in `triangle`.

3 Likes

The reason `@constCast` didn’t work is because references to comptime-known literals like `&1` or `&.{ 2, 3 }` will generally have their data stored in read-only segments of memory (there may be some instances where this is not true), which means that writes to those locations will usually segfault. So using `@constCast` is a no-go (and you should generally avoid reaching for that builtin unless you are dead certain it is required for your use case).

To get your slice of mutable triangle rows, you need to first make sure the actual triangle data is stored someplace where you have have write access, i.e. as a variable on the stack, a mutable global variable or as dynamically allocated memory.

At the very least, you need two arrays, one containing the triangle data and another for the slices. @Sze’s suggestion is a good one, but because this problem deals with a regular shape with some nice mathematical properties (Triangular numbers, notably the `n * (n + 1) / 2` part), you can simplify it further and initialize both the buffer containing the data and the buffer containing the slices/rows in relatively few lines of code:

``````const std = @import("std");

pub fn main() void {
const triangle_rows = 15;

// zig fmt: off
var triangle_data: [@divExact(triangle_rows * (triangle_rows + 1), 2)]u8 = .{
75,
95, 64,
17, 47, 82,
18, 35, 87, 10,
20,  4, 82, 47, 65,
19,  1, 23, 75,  3, 34,
88,  2, 77, 73,  7, 63, 67,
99, 65,  4, 28,  6, 16, 70, 92,
41, 41, 26, 56, 83, 40, 80, 70, 33,
41, 48, 72, 33, 47, 32, 37, 16, 94, 29,
53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14,
70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57,
91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48,
63, 66,  4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31,
4, 62, 98, 27, 23,  9, 70, 98, 73, 93, 38, 53, 60,  4, 23,
};
// zig fmt: on

var triangle: [triangle_rows][]u8 = undefined;
{
var start: usize = 0;
for (&triangle, 0..) |*row, i| {
const end = start + i + 1;
row.* = triangle_data[start..end];
start = end;
}
}

triangle[1][1] = 255;

std.debug.print("{any}", .{triangle});
}
``````
4 Likes