const E = enum { a, b, c, };
pub fn farr(arr: []const E) void {
std.debug.print("arr type: {s}\n", .{ @typeName(@TypeOf(arr)), }); // "slice", basically
}
test "array-or-struct" {
const a = .{ .a, .b, };
std.debug.print("a type: {s}\n", .{ @typeName(@TypeOf(a)), }); // "struct", basically
//farr(a); // obviously can't do this
farr(&a); // bit of a surprise
}
The advice I’d give to myself here is, “don’t do that. instead:
const a = [_]E{ .a, .b, };
… if, of course, that’s what you really mean.” But it raises questions about whether this is how it should work – are there cases for… well, what I might call implicit casting from struct to slice? It doesn’t work everywhere; for instance, I can’t ++ that original a with a slice. But is this a “feature” I missed before? Or an accident to stay away from? Or…?
While it uses the struct keyword in the language syntax, Zig actually calls this a Tuple, and tuples do share some behavior with arrays/slices, they have a len field, can be iterated with inline for loops and support ++ and ** (as long the other type matches). So it also makes sense that this coercion exists.
FWIW I’d love to have tuples behave more like arrays and coerce to them more readily, e.g. both this:
const a: u32 = 123;
const b: u32 = 456;
for (.{ a, b }) |i| { _ = i; }
and this:
const a: u32 = 123;
const b: u32 = 456;
for (&.{ a, b }) |i| { _ = i; }
will currently throw:
error: unable to resolve comptime value
for (.{ a, b }) |i| { _ = i; }
~^~~~~~~~
note: tuple field index must be comptime-known
instead of just coercing to [2]u32 or *const [2]u32 respectively which would be really nice IMO.
I think homogenous tuples even having the same memory layout as arrays is the de-facto status quo but it’s not (yet?) formalized (though the coercion from tuple to array/slice is already guaranteed to work).