Should allocators be passed in as a value or as a pointer?

Just a quick question: should allocators be passed in as a value or as a pointer? My understanding is that parameters passed in as a pointer are meant to be mutated, whereas parameters passed in as a value are meant to be untouched. Underneath, zig will decide whether the value is passed as a copied value or as a reference, but we don’t get to control that.

What’s the convention for passing in allocators? I find myself flipping back and forth as I’m passing allocators deeper into a composition of structs.

2 Likes

The allocator interface is very lightweight and it only has two pointer member variables:

// The type erased pointer to the allocator implementation
ptr: *anyopaque,
vtable: *const VTable,

Generally speaking, passing lightweight structures (such as this and slices) is done by value.

Note that the allocator implementation is pointed to by the allocator interface. You’re only copying a pointer to the actual implementation and a pointer to the virtual table.

4 Likes

To give you an idea of what’s common, there is only one instance in the entire Zig codebase (compiler, standard library, and tests) where a pointer to Allocator is used, and it’s not even the real std.mem.Allocator: test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig

(I searched using the regex \*(const )?(std\.)?(mem\.)?Allocator)

And this one instance is very likely a leftover from before “Allocgate”:

So, std.mem.Allocator is always passed by value in the Zig codebase.

6 Likes