If nothing else, maybe they can add some helpers in the standard lib or codegen in the standard tooling to make stuff like this easier ? So one need not manually code all the function pointer types for methods and the dispatch in the interface type .
Yes, Zig is meant to be a low-level programming language, but stuff like this is basic table stakes nowadays. People will just make custom code generators after Zig 1.0 to do this exact thing - and they will likely be dozens of such custom code generators with subtle differences.
This is an intentional limitation, because the language creator worries the feature would be abused.
But oh well, if you're using Zig (or any other language using auteur-driven development like Odin or Jai or C3) you've already signed up for only getting the features that the benevolent dictator thinks are useful. You take the good (tightly designed with no feature bloat) and the bad ("I consider this decision unlikely to be reversed").
I believe languages are all about bias. A language must represent the preference of the community using it.
We should all learn from the case of Lisp, which is the simplest language and likely the most expressible language. The community suffered from the serious fragmentation driven by the sheer simplicity of the language and tons of NIH syndrome. It took them 30 years to get a standard CL, and another 20 years to get a de facto standard package repository (QuickLisp).
Zig is trying to fix C, by delivering stuff already present in Modula-2, minus comptime, in a C like clothing.
The judge is still out there if it will become something unavoidable in some industry circles, or just yet another programming language to talk about.
From my point of view, being stuck in the past of systems languages design, during the AI age where the actual language becomes slowly irrelevant, it will mean the latter.
It was not a trap being an early Fortran adopter and envisioning what a couple of decades later would be the future of Assembly programmers.
I already do lots of programming where the underlying language hardly matters.
this is not really a problem I've encountered. did you have a specific case where you got puzzled?
I think it is table stakes for higher level programming language. Zig wants to be somewhat like high level assembly.
There is still someway to go before 1.0 so may be something will be done. I mean I am still uneasy with them focusing so much on async.
The benefits of the language outweigh the minor inconvenience.
So while as language geek, I will spend the time to understand and play with them long enough to be able to know what I am actually talking about, using said languages for project delivery only if it is a customer requirement of some sort.
do you have any evidence for this? what is the error you're proposing here?
I like Zig and eagerly await 1.0, just wish it was not quite so bare-bones.
I wrote a second blog for the C++ style vtable. https://williamw520.github.io/2025/07/17/memory-efficient-zi...
The first version is faster (one pointer indirection) with more memory consumed. The second version is more memory efficient sharing a single vtable for all instances, a bit slower (two pointer indirections).
We see:
pub fn implBy
Camel case (impl_obj: anytype)
Snake case v_setLevel
Mix of camel case and snake case anyopaque
whatever that ispub fn implBy(impl_obj: anytype) Logger
Function in a weird place. Normal practice is for the specific implementations to have a getLogger function to create the logger. For example, the allocators in Zig have you instantiate the top level and then call .allocator() on them to get the vtable + pointer to self.
It manages sanity a lot better, and the function implementations in the vtable can be private.
const intf = interface.implBy(implementation)
This makes it possible to write an interface against any implementation objects. E.g. I can write an interface against the standard hashmap's get() and put() methods. Why would I do that? Let's say I have my custom hashmap with get() and put(), but I want to use the standard hashmap as fallback. A common interface working against my custom hashmap and the standard hashamp is possible.Lol sure. It "doesn't need to know" it just happens to magically have the correct functions as public methods to exactly map into your vtable constructor.
It's much easier to not do this. And have your implementations provide the vtable as a method.
If your implementation doesn't provide a method for this you need to wrap it with an object that does. Which you've done but you've decided that wrapper object should be the interface itself which is weird.
This is called structural typing. Go and typescript have forms of it.
A simple example using traits (Rust):
// logger.rs
trait Logger {
fn log_something(&self);
}
// mycustomlogger.rs
struct MyCustomLogger;
// logger.rs or mycustomlogger.rs
impl Logger for MyCustomLogger {
fn log_something(&self) {
println!("Hello");
}
}
The actual `log_something` implementation for `MyCustomLogger` (ie, the block with the `println!` call) can be outside of the impl, as a function or inherent method, and just be called by the impl block - thus implementing similar "glue" as described in the article.The difference is that there is no strong requirement on where exactly it needs to be [1].
This would likely require Zig to support "interfaces" at the language level, though.
--
[1] PS: there is the "orphan rule" for trait implementations, but I'm simplifying it here for the sake of the example
EDIT: formatting
The Zig version ‘Interface1.implBy(Impl2)’ is a rough equivalent to ‘impl Interface1 for Impl2’. It did it within the constraints of Zig.
Yes. It's not as crazy as you might imagine, and it's more robust than the log delegator approach.
And using vtable in code feels cleaner compared to using generics
Do all code bases use the same pattern? Or does it not matter for interoperability, e.g. between an app and the libraries it consumes?
Unfortunately no - it's a complete free for all - which is the problem. Conventions are great to a point but language enforced constructs are better.