Hello,
I’ve noticed this difference of behaviour and would like to get explanations from seasoned Zig devs.
With the following code
const std = @import("std");
pub const Scheduler = struct {
type: type,
ptr: *anyopaque,
vtab: *const struct {
start: *const fn (ptr: *anyopaque) void,
stop: *const fn (ptr: *anyopaque) void,
},
pub fn start(self: Scheduler) void {
self.vtab.start(self.ptr);
}
pub fn stop(self: Scheduler) void {
self.vtab.stop(self.ptr);
}
fn init(obj: anytype) Scheduler {
const object_type = @TypeOf(obj);
const impl = struct {
fn start(ptr: *anyopaque) void {
const self: *object_type = @ptrCast(@alignCast(ptr));
self.start();
}
fn stop(ptr: *anyopaque) void {
const self: *object_type = @ptrCast(@alignCast(ptr));
self.stop();
}
};
return .{
.type = object_type,
.ptr = @constCast(&obj),
.vtab = &.{
.start = impl.start,
.stop = impl.stop,
},
};
}
pub fn get_default() Scheduler {
const implementation = DefaultScheduler.init();
return Scheduler.init(implementation);
}
};
const DefaultScheduler = struct {
name: []const u8,
pub fn start(self: DefaultScheduler) void {
std.debug.print("Hidden start implementation: {s} \n", .{self.name});
}
pub fn stop(self: DefaultScheduler) void {
const n = self.name;
std.debug.print("Hidden stop implementation: {s} \n", .{n});
}
fn init() DefaultScheduler {
return .{ .name = "DefaultScheduler" };
}
};
I have the following output
Hidden start implementation: DefaultScheduler
Hidden stop implementation: DefaultScheduler
However, if I remove the field type in Scheduler struct; as shown below
const std = @import("std");
pub const Scheduler = struct {
ptr: *anyopaque,
vtab: *const struct {
start: *const fn (ptr: *anyopaque) void,
stop: *const fn (ptr: *anyopaque) void,
},
pub fn start(self: Scheduler) void {
self.vtab.start(self.ptr);
}
pub fn stop(self: Scheduler) void {
self.vtab.stop(self.ptr);
}
fn init(obj: anytype) Scheduler {
const object_type = @TypeOf(obj);
const impl = struct {
fn start(ptr: *anyopaque) void {
const self: *object_type = @ptrCast(@alignCast(ptr));
self.start();
}
fn stop(ptr: *anyopaque) void {
const self: *object_type = @ptrCast(@alignCast(ptr));
self.stop();
}
};
return .{
.ptr = @constCast(&obj),
.vtab = &.{
.start = impl.start,
.stop = impl.stop,
},
};
}
pub fn get_default() Scheduler {
const implementation = DefaultScheduler.init();
return Scheduler.init(implementation);
}
};
const DefaultScheduler = struct {
name: []const u8,
pub fn start(self: DefaultScheduler) void {
std.debug.print("Hidden start implementation: {s} \n", .{self.name});
}
pub fn stop(self: DefaultScheduler) void {
const n = self.name;
std.debug.print("Hidden stop implementation: {s} \n", .{n});
}
fn init() DefaultScheduler {
return .{ .name = "DefaultScheduler" };
}
};
I’ve the following behaviour in the console
Hidden start implementation: Hidden stop implementation: