As part of testing my solution to Advent Of Code 2024 day 5, I'm trying to create a struct containing an array created from an literal, but I'm getting an error that I can't resolve:
const std = @import("std");
const print = std.debug.print;
const Update = struct { values: []u32 };
test "Create and operate on an Update" {
const update = Update{ .values = [3]u32{ 1, 2, 3 } };
print("DEBUG - update is {}\n", .{update});
}
$ zig test scratch.zig
scratch.zig:7:40: error: array literal requires address-of operator (&) to coerce to slice type '[]u32'
const update = Update{ .values = [3]u32{ 1, 2, 3 } };
~~~~~~~~~~~~^~~~~~~~~~~~~~~
That seems clear enough - except that when I follow the advice, I get a type-mismatch:
const update = Update{ .values = &[3]u32{ 1, 2, 3 } };
$ zig test scratch.zig
scratch.zig:7:29: error: expected type '[]u32', found '*const [3]u32'
const update = Update{ .values = &[3]u32{ 1, 2, 3 } };
~^~~~~~~~~~~~~~~~~~~~~~~~~~~
That makes sense - so far as I can understand, &
converts a variable to a pointer, so it's correct for the compiler to complain about being passed a *[]u32
when it expects a []u32
. So, both options give me an error - how can I pass a []u32
to the Update
constructor?
After reading David Vandersons Zig notes, I think I have a better idea of what's going on:
[]u32
is a slice of u32
s, not an array thereof.
Update
s much have values
of the same size.Update
like so:const Update = struct { values: []u32 };
test "Create and operate on an Update" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var values = try allocator.alloc(u32, 3);
defer allocator.free(values);
values[0] = 1;
values[1] = 2;
values[2] = 3;
const update = Update{ .values = values };
print("DEBUG - update is {}\n", .{update});
}
Which works as-expected. Is this the idiomatic way to do this? I'm surprised there's no built-in way to define a "slice-literal" as there is for arrays - 5+n lines to define an n-length slice (or 3+n lines if you already have an allocator available) seems verbose, to me!
[(number here)]u32
are arrays, but []u32
are slices (fat pointers).
If you update your struct to use an array of 3 elements ([3]u32
), then your test code will work correctly.
If you had an immutable slice ([]const u32
), you could use this syntax: &.{ 1, 2, 3 }
. The documentation lists it as "alternative initialization using result location".
But since you need the slice to be mutable, you need to allocate memory for it. Hence using an allocator is the right approach here.