Zig Code Smells

The problem is that interfaces like Reader and Allocator are for runtime polymorphism. They have the cost of the indirection and if they need state, they necessarily have reference semantics, since the interface can’t store the state inside of it. In the vast majority of cases, the call site knows exactly the concrete type that they are going to use with the functions, and that never changes throughout the runtime. So you’re paying theses costs for nothing.
I actually think the Allocator idiom is something Zig needs to rethink. One the things they say in the C++ community is that languages that don’t have virtual polymorphism end up with ad hoc implementations of virtual polymorphism. And when you learn Zig, one the first things you encounter is… The Allocator, an ad hoc implementation of virtual polymorphism. It literally has a member called vtable.
I think allocators should be passed by anytype, and the allocators should implement the alloc and free methods, so that you can call them directly instead of being forced to go through the indirection. This would lead to much better generated code.
Before anyone mentions it, devirtualization is a myth. It never happens. I looked at the generated machine code, and I could never get it to trigger, neither in C++ nor Zig.
Taking by anytype is strictly superior than taking by interface in terms of generated code, because it avoids the cost of indirection and, if the user actually wants runtime polymorphism, they can simply call the function with a type erased object, which is exactly what all those interfaces are. This is the famous “pay only for what you use”. You can also use a type erasure if your goal is actually to avoid code bloat, which can happen with generic code. By consistently calling the same function with the same type erased object, you only instantiate one function.
I understand the choice Zig made. One the things people complain about C++ is that generic code can lead to unreadable code and code bloat. By using interfaces in its standard library, I believe Zig was trying to say “hey, we have the generics, but it’s not going to become the mess that you see in C++”. Sort of like hiding its generics so that people don’t see it so blatantly and avoid comparisons with C++. But, the reality is, anytype is the superior choice for performance.
anytype loses in terms os readability and tooling, that much is certain. But a lot of that could be improved with concepts. They are not necessary for generated code, their only purpose is programmer ergonomics. I thought that the rejected proposal for concepts was beautiful, readable and matched the whole idea of Zig quite well (can’t find the proposal now).

3 Likes