This is exactly Zig's strength, not its problem. The flexibility/lack of interfaces allows you to choose the correct abstraction for the given task. In C++, every writer is `anytype`, in Java every writer is `AnyWriter`, in Rust every writer is `GenericWriter`. They all have tradeoffs but "fits better due to language design" shouldn't be one of the tradeoffs considered.
I may be misunderstanding the article - but it looks like GenericWriter in zig still has dynamic dispatch overheads at runtime in all cases. Rust traits are more like “anytype” - since they get monomorphized by the compiler and have no runtime overhead at all. But unlike zig’s anytype, traits have excellent documentation (since they’re explicit, not implicit interfaces). Rust can also implicitly create an “AnyWriter” style object if you don’t want monomorphization via &dyn Trait. But you often don’t need to, because you can store trait objects in other structs just fine. - Though admittedly, you can do the same in zig via comptime structs.
There are a lot of things I admire about zig. But for interfaces like Writer, rust’s trait system seems like the better tool. I wish zig would copy rust’s trait system into the language.
No, GenericWriter takes a function at compile time and it gives you a GenericWriter struct that calls that function (at compile time), no function pointers needed.
There's definitely overhead with the GenericWriter, seeing as it uses the AnyWriter for every call except `write` (1)
genericWriter - 31987.66ns per iterations
appendSlice - 20112.35ns per iterations
appendSliceOptimized - 12996.49ns per iterations
`appendSliceOptimized` is implemented using knowledge of the underlying writer, the way that say an interface implementation in Go would be able to. It's a big part of the reason that reading a file in Zig line-by-line can be so much slower than in other languages (2)
I was curious, so I ran your zig version myself and ported it to rust[1].
I think you forgot to run your benchmark in release mode. In debug mode, I get similar results to you. But in release mode, it runs ~5x faster than you reported:
genericWriter - 4035.47ns per iterations
appendSlice - 4026.41ns per iterations
appendSliceOptimized - 2884.84ns per iterations
I bet the first two implementations are emitting identical code. But appendSliceOptimized is clearly much more efficient.
For some reason, rust is about twice as fast as zig in this test: