Few weeks ago I came with the idea of learning Zig. I went to ziglearn and read it all, but I felt I needed something more hands on, something that let me explore and discover the language on my own.
After some though I decided to give Crafting Interpreters a try, it has always been on my reading list, it was the time. Crafting interpreters is a book where you implement two versions of the same language, called Lox, using two different techniques. The III part of the book implements the Lox programming language using C, and I though it would be the ideal project to learn more about Zig and interpreters, two birds one rock!
I want to write my learnings and findings during this journey, my idea is to have a post for each chapter of the book highlighting specific things that I personally found interesting about Zig and how it differs from the solution proposed by the book. I will probably not talk much about interpreters or compilers since I now next to nothing about it. You can find the code here https://github.com/avillega/zilox. I decided to name my version of the interpreter zilox given that the original version is called clox. If you do some pronunciation games you can pronounce both of them as see-lox.
One of the first few things the author does in the first chapter of the III part is to implement a dynamic sized array for storing bytes. Awesome! Zig has an ArrayList struct as part of the std, but the goal here is to learn so I decided to follow along and try to implement my own dynamic sized array to store u8
, all went well and good until I found this function signature of the realloc
function in Allocator.zig
pub fn realloc(self: *Allocator, old_mem: anytype, new_n: usize) t: {
const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
break :t Error![]align(Slice.alignment) Slice.child;
}
I asked what that return type means in the zig sub reddit r/zig and they broke it our for me. Basically the block after the t:
is defining t
, duh (It was not easy to me to understand that at first), and the block is saying, "I expect the type of old_mem
to be a pointer type and will return a Slice of the same type and same aligment as old_mem
. if you have a slice of type []u8
Slice.child
will be u8
.
The second thing the author does in the same chapter is to implement a second dynamic sized array, this time to store f64
values. What! again? C story of generic programming is not the best, but in Zig that is not the case. I decided to go with my limited knowledge and implement a generic dynamic sized array that I can reuse to store u8
, f64
and any other type. I got to say that implementing this made generic programming in Zig click for me, it is powerful, it is simple, it is elegant, no weird <T>
every where, no special syntax, just plain old functions that take types as arguments and return a specific type. This is basically what I defined.
pub fn DynamicArray(comptime T: type) type { ... }
And this is how I use it.
const Value = f64;
const BytesArray = DynamicArray(u8);
const ValuesArray = DynamicArray(Value);
And guess what the author does third, exactly!, another dynamic sized array, this time to store the line numbers of the Lox program. With my generic DynamicArray implemented, it was as easy as doing.
const LinesArray = DynamicArray(u16);
Almost a whole sub section of the chapter, becomes a couple lines of code.
So far I've like the experience of writing Zig a lot, I've gone through the code of the std
to learn and to understand more about Allocators, Dynamic Arrays (ArrayLists), strings, formatting, etc. The std
code is very approachable and comments are very useful. I personally have found reading the source code a bit more helpful in understanding what is going on than the docs themselves.
I hope you find this article helpful and see you in the next one.
Cover Photo by Ryutaro Tsukata from Pexels.
Latest comments (4)
That function signature always looked weird to me too - thanks for explaining it!
Also you can use
here to colorize codeblocks if you want.
I am doing this for my new posts, Thanks!
while my mind is blown by the realloc return signature i wonder if it wouldn't be more readable as a function call?
this is a great exercise! i did the same a couple of years ago. back then the book wasn't completed, and Zig was different too... but it lets you really appreciate how Zig improves on C while keeping the same "hands on" feeling (but the standard library has grown quite a bit too!)