Having init() pass back a memset struct

I’m trying to write an init() function for PackedThing1 which passes back an instance where all struct variables are set to 0xFF (or at least all bits on for the type).

Can anyone explain why init2() doesn’t do this. I’m sure I’ve misunderstood something.

init1() creates a PackedThing1 with default values which is copied on the return, it gets printed as I’d expect.

init2() creates an undefined PackedThing1 which it then tries to @memset() to 0xFF, but I get garbage back.

const std = @import("std");

pub const PackedThing2 = packed struct {
    a: u4 = 0xA,
    b: u4 = 0xB,
    c: u4 = 0xC,
    d: u4 = 0xD,
};

pub const PackedThing1 = packed struct {
    const Self = @This();
    a:u32 = 123,
    b:u32 = 456,
    c:PackedThing2,

    pub fn init1() Self {
        // pass back a PackedThing1 with default values
        const r:Self = .{.c = .{}};
        return r;
    }

    pub fn init2() Self {
        var r:Self = undefined;
        // pass back a PackedThing1 with 0xFF values???
        @memset(std.mem.asBytes(&r), 0xFF);
        return r;
    }
};

pub fn main() !void {
    var thing = PackedThing1.init1();
    std.debug.print("thing={any}\n", .{thing});
    var bytes = std.mem.asBytes(&thing);
    std.debug.print("len={d} {X}\n", .{bytes.len, bytes});

    thing = PackedThing1.init2();
    std.debug.print("thing={any}\n", .{thing});
    bytes = std.mem.asBytes(&thing);
    std.debug.print("len={d} {X}\n", .{bytes.len, bytes});
}

For the second set of printing, I get len=16 { FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, 0, 0, 0, 0, 0, 0 }

0xFF is not zeros, it is all ones.

Sorry. Good point, it’s still not doing what I’d expect. I’ll update

Ok, I think I confused myself.

I guess it’s just padding the struct at the end, but otherwise is doing what I’d expect.

And so to get at the “correctly” sized struct data, I need to do

std.debug.print("len={d} {X}\n", .{bytes.len, bytes[0..@bitSizeOf(PackedThing1) / 8]});

1 Like

I suggest not doing this kind of bit twiddling without justification.

By which I mean that the natural expression of “set all fields to 0xff” is to just set all the fields to that value. That does mean you need to keep the init function in sync with the fields, but it’s much clearer what’s going on, and why.

Justification would be decompiling both approaches in the useful release mode, and seeing that the @memset version is significantly simpler once compiled. Compilers aren’t magic, but they’re reasonably good at turning “set each field to the same value” into “blit the value straight into the memory region”.

Going behind the type system’s back like this can create extremely subtle problems which are very hard to debug. It can also end up deoptimizing code, oddly enough, because you’re telling the compiler: at this point, this is not a struct, it’s a region of bytes, and the only valid thing to do is represent it exactly that way here. That kind of thing can limit its ability to help you, sometimes significantly.

You could actually write a type-safe version of this which keeps up with changes to the field definitions using an inline for loop, if you wanted. The effort involved may or may not be justified.

2 Likes

I was trying to write code to ensure all members of the struct were initialised to zero. That then led to confusion around the size of the struct.

I see now that it’s better to just give the struct fields default values of zero and initialise it to .{} than try to memset as I would in C.

1 Like

There’s also std.mem.zeroInit and its frowned-upon cousin zeroes. Zig philosophy discourages defensive or automatic initialization to zero, but of course there are data structures where zeroed-out is the correct starting state. If this is one of those, then that’s how to get it.

1 Like

You may also be interested in the default value conventions expressed in the language reference:
https://ziglang.org/documentation/master/#Faulty-Default-Field-Values

2 Likes

I also have a PR open to improve the description of packed struct memory layout:

2 Likes