I’ve been playing with interfaces as described in David Vanderson’s post, and I have decided to replace fn interface(self: *PickRandom) Interface
with an instance of Interface
stored in PickRandom
, initialized in init()
.
Strangely, my code crashes in random ways, but if I add a line to pub fn pick(self: *const Interface) i32
that accesses self
or self.impl
(probably must access self
), the bug goes away
Could somebody please help me understand why is this happening?
Here’s the code (yes, I’m probably doing it wrong in PickRandom
’s fn init()
, as what it returns is a copy of me
, which means the pointer is wrong, but the question is about the magic of std.debug.print()
):
const std = @import("std");
pub fn main() void
{
var pick_random = PickRandom.init();
ifaceUserFn(pick_random.iface);
}
pub fn ifaceUserFn(iface: Interface) void
{
for (1..4) |i|
{
const p = iface.pick();
std.debug.print("ifaceUserFn() call #{d} returned {d}\n", .{ i, p });
}
}
const Interface = struct
{
impl: *anyopaque, // pointer to the implementing struct (we don't know the type)
pickFn: *const fn (*anyopaque) i32, // can call directly: iface.pickFn(iface.impl)
pub fn pick(self: *const Interface) i32 // allows calling: iface.pick()
{
// Uncommenting the line below doesn't make the bug go away
//std.debug.print("pick()\n", .{});
// But uncommenting any other line does
//std.debug.print("pick() - iface is {*}, iface.impl points to {*}\n",
// .{ self, self.impl });
//std.debug.print("{*}\n", .{ self });
const a = self.pickFn(self.impl);
//std.debug.print("pick() - iface is {*}, iface.impl points to {*}\n",
// .{ self, self.impl });
//std.debug.print("{*}\n", .{ self });
return a;
}
};
const PickRandom = struct
{
rnd_gen: std.rand.DefaultPrng, // specific to PickRandom
iface: Interface,
fn init() PickRandom
{
var me = PickRandom
{
.rnd_gen = std.rand.DefaultPrng.init(0),
.iface = undefined,
};
me.iface =
.{
.impl = @as(*anyopaque, @ptrCast(&me)),
.pickFn = myPick,
};
std.debug.print("init() - new PickRandom = {*}, its iface.impl points to {*}\n",
.{ &me, me.iface.impl });
return me;
}
fn myPick(self_void: *anyopaque) i32
{
// cast typeless impl pointer to ourselves
var self = @as(*PickRandom, @ptrCast(@alignCast(self_void)));
return self.rnd_gen.random().intRangeAtMost(i32, 10, 20);
}
};
Errors I get if I don’t access self
or self.impl
in pub fn pick()
:
Segmentation fault at address 0x0
/home/archie/projects/zig-playground/iface_02b_stripped.zig:69:9: 0x2220f9 in myPick (iface_02b_stripped)
return self.rnd_gen.random().intRangeAtMost(i32, 10, 20);
^
???:?:?: 0x6c422e76504b451 in ??? (???)
Unwind information for `???:0x6c422e76504b451` was not available, trace may be incomplete
Aborted (core dumped)