Zig Patterns: Zero Values
“Make the zero value useful.”
Rob Pike (Go Proverbs)
There’s a lot of useful information for any programmer packed into Go conference talks, but today I’m focusing on this quote from Rob Pike.
The idea is that you can reduce the API that the programmer using your code has to parse if you can make the default state of a structure useful. No need to remember to call an .init()
function, and if you can build primitives that have useful zero values, then you can compose these to create larger, more complex concepts that also have that same property.
Go doesn’t support default values for struct properties: integers will default to 0
, maps will default to nil
, and so on. In Zig, however, we do have this ability, so we have a bit more flexibility than Go in this regard.
An example from the standard library is once again our friend std.http.HeadParser
. Internally it keeps a state about whether it has seen the end of the HTTP header, but this gets defaulted to .start
, so users of it can use it like:
var parser: HeadParser = .{};
const bytes_read = parser.feed(bytes);
More recently in the Zig source I’ve started to see a pattern pop up where the desired default value is exposed as a constant called init
. For example, the std.heap.DebugAllocator
follows this pattern:
pub fn DebugAllocator(comptime config: Config) type {
return struct {
// ...
const Self = @This();
pub const init: Self = .{};
// ...
};
}
Using a declaration like this instead of relying on the default initialization can help to prevent accidental bugs from incorrectly initialized struct fields. This is especially useful if the struct changes over time, like the code in the Zig standard library likely will.