Using arrays in packed structs

In Zig, packed structs can’t contain arrays (Packed structs can't contain arrays · Issue #12547 · ziglang/zig · GitHub).

I need to create a chunk of data which does have a repeating instance a fixed number of times - an array.
If I inline all of the array elements into the struct as f0 … f99, is there then some way to read and write those named fields via an index without writing out:

switch(index) {
    0 => return self.f0,
    1 => return self.f1,
    etc.
}

Is there some combination of inline and @field() which can do this?

Or is there a more natural way to achieve it?

const std = @import("std");

pub const PackedThing1 = packed struct {
    const Self = @This();
    f0: SomeType
    f1: SomeType,
    f2: SomeType,
    f3: SomeType,
    f4: SomeType,
    ...
    f99: SomeType,

    pub fn init(someRuntimeData:i32) Self {
        // do something with runtime data and populate f0..f99
        ???
    }
};

Something like this?

const struct_fields = @typeInfo(PackedThing).@"struct".fields;
inline for (struct_fields, 0..) |field, i| {
    if (i == param_index) {
        return @field(a_strcut, field.name);
    }
}

Probably better to use an inline else clause in a switch statement actually.

2 Likes

Thanks, this works. One minor correction, it’s @"Struct"

On v0.13 it’s Struct (no @"" needed). On v0.14 several Types were reworked to match the style guide so it’s now @"struct" (w/ @"" being needed because struct is a keyword, though I think there’s an issue pushing for some sensible exceptions to that rule).

2 Likes

Note that packed struct isn’t bytepacked (aka same as packed attribute in C). Packed struct has unfortunate name in zig, but it basically means your struct will be backed by a integer, and is very useful for implementing bitfields for example. If you use packed with a large struct (larger than u64 usually), you’ll end up with pretty bad codegen. You might want to consider aligning your fields instead if you want bytepacked, and extern struct if you need defined layout.

6 Likes

You made me finally understand why Zig has both packed and align, thanks!

2 Likes

It definitely requires caution, and understanding what large integers in Zig are all about. You don’t want weird stuff like a u17 field starting at bit 60 of a u80 backed struct, the compiler will dutifully follow the documented behavior here and the result will be inefficient.

But if you need a collection of 128 u2 enums, a packed u256 struct is going to be just about as efficient as any other approach to the same problem space, while being considerably easier to write code for. I’d be asking myself if I could get away with a byte each and just using [256]u2 instead, this is an ordinary sort of tradeoff between compactness and efficiency in working with small quantities.

I agree that the premise that a packed struct larger than u64 is probably the wrong data structure is a good place to start, and that frequently a hand-aligned non-packed struct is a better choice.

2 Likes