Zeroing at comptime syntax

I was recently solving Advent of Code 2015 Day 7 and I stumbled onto this little tiny rabbit hole. So, I have a struct called wire such as:

const Source = union(enum) {
    id: [2]u8,
    val: u16,
};

const Wire = struct {
    id: [2]u8 = .{ ' ', ' ' },
    val: ?u16 = null,
    gate: enum(u8) { RShift, LShift, And, Or, Not, Value } = .Value,
    lSrc: Source = .{ .val = 0 },
    rSrc: Source = .{ .val = 0 },
};

Since I didn’t want to use a map because I’m allergic to STDs, I decided to create a big ass array and just iterate it over and over to search the wires. My first attempt at creating such array was a naive undefine:

var wires: [512]Wire = undefined;

Since undefined makes the memory layout be a 0101 pattern, my nullable value became very not null, which is not fun because I really need that value to be null since, later in the solution, it’s non-nullesness implies it has been set.

Then I tried:

var wires: [512]Wire = std.mem.zeroes([512]Wire);

Which fails at comptime because it Can't set a 15-07.Source to zero. I guess tagged unions can’t be zeroed…

Then I checked my ziglings exercises and found this weird syntax:

var wires: [512]Wire = [_]Wire{.{}} ** 512;

This does work, but it is really ugly.


Before you say it, yes, I know I can just @memset(std.mem.asBytes(&wires), 0); at runtime, but I wanted this to be done at comptime.

Some questions that I would appreciate an answer to would be:

How would you have written this?
Is there a simpler way to zero at comptime?
Am I expected to zero stuff at runtime?
Is there a way to zero a tagged union?
Why cant I just var wires: [512]Wire = zero;!?

1 Like

@splat

3 Likes

Well that was fast. Thanks!

Just for future reference, I asume the syntax would be:

var wires: [512]Wire = @splat(.{});

You’re welcome! That’s correct.

Also I noticed the @splat section in the langref made an incorrect claim about what element types are allowed, so I removed it:

https://codeberg.org/ziglang/zig/commit/072744f1ac8f4f58a80dc6ca0e15bd4ecbb4abfb

3 Likes

Yup, I was going to say that I assumed it only worked with numerical types but I didn’t want to sound stupid in case there was something I was not seeing.

Also, I only recall numerical (u8 included) examples on the ziglings. I might poke them and ask for weirder examples just as this one.

I recommend checking disassembly to see what kind of code this generates. In some cases the compiler is smart enough to optimize out the constants from the binary, but in many cases you bloat your binary vs. using memset.

This is bit poor example, but showcases what can happen:

2 Likes

Is there a matching issue for this to be fixed?

1 Like

Oh god. Yeah that’s a very confusing resolution.

Hey I don’t wanna brag, but I have been splatting all around since I got this info:

const Dir = packed struct(u16) {
    node_idx: u4 = 0,
    distance: u12 = 0,
};

const Node = struct {
    id: [16]u8 = @splat(' '),
    net: [8]?Dir = @splat(null),
    seen: bool = false,
};

const nodes: [16]Node = @splat(.{});

It has quickly become my handy battle-axe to handle each and every slice initialization, and I don’t think it’s usefulness stops there, given that I’m barely touching vectors yet.

Just an awesome function.

3 Likes

Yep, I splat a lot too. In most cases I prefer to declare a separate const empty in my struct.
I’m even thinking about discarding all default values in each struct I write.

5 Likes