Description
In Zig the compiler prevents you from passing structs (and other potentially big data) to procedures by values. This is good, cause then we avoid large copies. This does however have the side effect that all generic code have to pass &const T
around, to ensure that the code is truly generic.
Now, let's say that I have some procedure for lessThan
on strings ([]const u8
). It would look something like this:
fn lessThan(lhs: []const u8, rhs: []const u8) -> bool { ... }
And I have a slice of strings ([][]const u8
). I can't use my lessThan
procedure for generic procedures like insertionSort
, because it requires procedures of type fn(&const T, &const T) -> bool
or fn(&const []const u8, &const []const u8) -> bool
for strings specifically. This means, we have to write an ugly wrapper function whenever we wanna use "pass by value" procedures with generic procedures.
This might not be a problem, and we decide that this is indeed how it should be. I do however think this needs to be discussed before deciding on "the standard way" of writing generic procedures.
Let me kick the discussion of with one way of dealing with this. In Zig, we can actually write a user space procedure that can decide whether a type can be passed by value. With this a procedure can also be written to take a type, and return a type that can be passed to a procedure (see types.zig for implementation).
With these procedures insertionSort
can now have this signature:
fn insertionSort(comptime T: type, items: []T, lessThan: fn(lhs: Pass(T), rhs: Pass(T))->bool)
And now, insertionSort
can take my lessThan
procedure without any wrapper.
This solution does complicate the generic code a little, as one would have to call the getValue
to use values passed with Pass
.