Const *S versus var *S

Yeah, good point - forgot about that little detail. You can do this and it works:

const std = @import("std");
const print = std.log.info;

const S = struct {
    x: u32 = 10,
};

fn create(T: type, comptime handle: anytype) *T {
    return &struct {
        var o = blk: {
            std.mem.doNotOptimizeAway(handle);
            break :blk std.mem.zeroInit(T, .{});
        };
    }.o;
}

pub fn main() void {
    var vp = create(S, opaque{});
    const cp = create(S, opaque{});
    vp.x = 20;
    cp.x = 30;
    print("vp.x = {d}", .{vp.x});
    print("cp.x = {d}", .{cp.x});
}

I forgot that if you don’t use the argument, it gets discarded and it can get folded into one instance. In this case, we are effectively throwing it away, but in a way that is opaque to the compiler.

Also, you asked if there is any motivation to this style of function. One affordance is that we can get the pointer through a pointer instance or we can re-reference the same thing using the same handle. This is well behaved:

pub fn main() void {
    var vp = create(S, 0);
    const cp = create(S, 1);
    vp.x = 20;
    cp.x = 30;
    print("vp.x = {d}", .{vp.x});
    print("cp.x = {d}", .{cp.x});
    print("(0).x = {d}", .{create(S, 0).x});
    print("(1).x = {d}", .{create(S, 1).x});
}

So here, I can get the same struct again using my key value 0 or 1 (or any other comptime known unique value). It’s an interesting detail that may be worth something (you could achieve something similar in other ways, too).

4 Likes