Zig For the Uninitiated: Zig Interfaces
By Tyler
Hey all, I’ve posted a new video. This one talks about how interfaces are handled in zig.
Interfaces are ways to formally declare a set of functionality that a data structure can provide. Why this is nice, is that you can write code that just requires an object implement the interface, and not care about the details of the data structure. In this way many structures can be used interchangeably.
Most languages provide nice syntax support for this. Zig however does not. There are practical and philosophical reasons for this1. In particular, syntax support is not required for creating interfaces. The Zig maintainers believe that having syntax for implementing interfaces, incentivizes programmers to use them too often, and not understand the performance implications associated with them.
What are the performance implications? Well the way interfaces are implemented in pretty much all languages, is through creating what is called a “Virtual Method Table”. When you define an interface, you are creating a new struct type, that contains function pointers for each method on the interface. For example, in Java the following interface:
public interface Foo {
public void bar();
public int baz();
}
Could be translated into zig as:
const Foo = struct {
bar: *const fn () void,
baz: *const fn () isize,
}
Then what actually happens when you implement that interface in Java, is that the java compiler adds a hidden field onto your class that contains a pointer to an instance of that vtable, with the function pointers referencing the implemented functions.
public class Fizz implements Foo {
String Data
int MoreData
// HIDDEN: Vtable
public void bar () {...}
public int baz () {...}
}
Since that is all that is happening, in Zig you can do that by hand. And indeed that is
how the std.mem.Allocator
interface works. Zig wants you to be able to switch out allocators
as you need to. The big allure is that you can use the same functions and data structures
without being locked into one memory allocation strategy. However that means that there
must be some sort of interface. And that is What zig Allocators do. They implement
the Interface by implementing the three required functions (alloc
, resize
, and free
),
and then creating a function (often allocator
) that will create a vtable containing pointers to
those functions.
Watch the video for more explanation!
-
I do want to note that the maintainers of zig have left the door open for a possible implementation in the future, but they want it to be good. ↩︎