Local Random Number Generator sometimes core dumps2

must be doing something silly here but if I compile the following with zig build runs core dump, but if I compile it with zig build -Doptimize=ReleaseFast it runs just fine. I’m not an expert in reading objdump yet so I was wondering.

  • The optimizations are clearly moving things but what?
  • And why can’t I set up a rng in a struct?

Later I came to my senses and realized that for me at least randomization setup can be done globally in a module, so I’m more curious than anything else.

Oh yea, v0.14.0-dev.2628+5b5c60f43

const std = @import("std");

const SIZE = 16;

const What = struct {
    allocator: std.mem.Allocator,
    stuff: []bool,
    prng: std.Random.DefaultPrng,
    rand: std.Random,

    fn init(allocator: std.mem.Allocator) !What {
        const stuff = try allocator.alloc(bool, SIZE);
        errdefer allocator.free(stuff);

        @memset(stuff, false);

        var prng = std.Random.DefaultPrng.init(123123);
        const rand = prng.random();

        return .{
            .allocator = allocator,
            .stuff = stuff,
            .prng = prng,
            .rand = rand,
        };
    }

    fn deinit(self: What) void {
        self.allocator.free(self.stuff);
    }

    fn do_it(self: *What) void {
        for (0..SIZE) |i| {
            self.stuff[i] = self.rand.boolean();
        }
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var what = try What.init(allocator);
    defer what.deinit();

    what.do_it();

    for (0..SIZE) |i| {
        std.debug.print("item {any}\n", .{what.stuff[i]});
    }
}

@Web maintainer: Sorry about the double post.

I also notice that even in the non-crashing version the RNG isn’t setting anything. I.e. all of the values are false. I haven’t figured that out either.

From your init function, you are returning a struct instance that contains a reference to the prng state (via rand, which contains a pointer to prng). This prng state only exists on the init stack frame; as soon as you return and call a different function, the memory that rand points to will be overwritten by something else, which will likely result in a segfault. If you get different results with different optimization modes it’s likely just a side effect of them optimizing function calls differently.

Structs that contain pointers that reference themselves (in this case the rand field pointing to the prng field) can be difficult to set up and are very prone to problems if they are copied around. I would suggest one of the following alternate designs:

  • Remove the prng field and instead take a std.Random as an argument to init. That way, the caller has full control both over the RNGs lifetime and the RNG implementation itself.
  • Remove the rand field, and only store prng. When you later want the std.Random interface, you have to get it via what.prng.random().
3 Likes

Perfect. Thank you.