Initial commit

This commit is contained in:
2026-04-05 18:21:48 -05:00
commit ab8ef15512
5 changed files with 118 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.zig-cache
+5
View File
@@ -0,0 +1,5 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
_ = b; // stub
}
+7
View File
@@ -0,0 +1,7 @@
.{
.name = .zig_references,
.version = "0.0.1",
.minimum_zig_version = "0.16.0-dev.3041+3dc5f1398",
.paths = .{""},
.fingerprint = 0xab9d2920d99601df,
}
+101
View File
@@ -0,0 +1,101 @@
const std = @import("std");
/// Here `Animal` is our interface that we want to implement. It itself is a struct
/// whose state will map the functions calls back to the parent struct which holds
/// it.
pub const Animal = struct {
// ageFn is the function which `age` calls. The function uses a `const` Animal
// which makes sure we don't change the underlying Animal.
ageFn: *const fn (self: *const Animal) usize,
// setAgeFn is the function we can set which is called by `setAge` in the
// interface.
setAgeFn: *const fn (self: *Animal, age: usize) void,
// Here we have an optional function with a default implementation when
// not specified. Check out `weight`
weightFn: ?*const fn (self: *const Animal) f64,
// These functions "drive" the interface by calling the setable functions
// above. The only exciting thing here is weight, where the default
// implementation is used.
pub fn age(self: *const Animal) usize {
return self.ageFn(self);
}
pub fn setAge(self: *Animal, newAge: usize) void {
return self.setAgeFn(self, newAge);
}
pub fn weight(self: *const Animal) f64 {
// here we check if the function for weight has been implemented. If it
// hasn't re utilize the underlying default implementation. This is
// done at runtime
// TODO: how would you do this at comptime
if (self.weightFn) |weightFn| return weightFn(self) else return self.defaultWeight();
}
fn defaultWeight(_: *const Animal) f64 {
return 0.0;
}
};
// Now we create an implemetor of the interface, a Dog, which is an animal.
pub const Dog = struct {
dogAge: usize,
// the interface exists as a field on the implemtenting struct. When we have
// a pointer to the field `interface` we can calculate the pointer to the parent
// `Dog`. Since zig uses LLVM there is no knowing how the compiler actually stores
// the struct in memory. We will see how to get around that soon.
interface: Animal,
pub fn init() Dog {
return .{
.dogAge = 10,
// When creating the interface we have to manually map each function
// to the interface. Here we reference the adress of each of these
// fuctions, but we don't carry the context of _what_ parent to call
// it for. That is why we have to explicity call the first argument
// in the `age` function:
//
// ```zig
// return self.ageFn(self);
// ^ explicit reference
// ```
.interface = .{
.ageFn = Dog.age,
.setAgeFn = Dog.setAge,
.weightFn = null,
},
};
}
// Now we are in the function, `a` is the animal pointer to the field we care
// about, and we can call @fieldParentPtr to get pointer of the parent Dog.
// from there we can finish implementing the interface.
// Notice the `Animal` is referenced as the parent since the calling context
// is relative to that field, not the parent object.
fn age(a: *const Animal) usize {
const prt: *const Dog = @fieldParentPtr("interface", a);
return prt.dogAge;
}
// We can also use a `var Animal` so we can manipulate the parent object as well
fn setAge(a: *Animal, newAge: usize) void {
var prt: *Dog = @fieldParentPtr("interface", a);
prt.dogAge = newAge;
}
};
test "intrusive interface" {
const testing = @import("std").testing;
var dog = Dog.init();
// we can call it now! you can see we call `dog.interface.age` since the function
// first argument is the interface itself, and not the underlying struct
try testing.expectEqual(10, dog.interface.age());
// This would also work, though is a little odd, but it might highlight what is
// going on under the covers.
try testing.expectEqual(10, Dog.age(&dog.interface));
dog.interface.setAge(15);
try testing.expectEqual(15, dog.interface.age());
try testing.expectEqual(0.0, dog.interface.weight());
}
+4
View File
@@ -0,0 +1,4 @@
test "Everything" {
_ = @import("intrusive_interface.zig");
@import("std").testing.refAllDecls(@This());
}