Early talks by Andrew explicitly leaned into the notion that "software can be perfect", which is a deviation from how most programmers view software development.
Zig also encourages you to "think like a computer" (also an explicit goal stated by Andrew) even more than C does on modern machines, given things like real vectors instead of relying on auto vectorization, the lack of a standard global allocator, and the lack of implicit buffering on standard io functions.
I would definitely put Zig on the list of languages that made me think about programming differently.
The big thing I would say I actually learned and would intentionally apply to other languages is SIMD programming. Otherwise, I'd say it gave me a much clearer mental model of memory management that helps me understand other languages much more fundamentally. Along with getting my hands directly on custom allocators for the first time, a question that took me time to figure out but gave me a lot of clarity in answering was "why can't you do closures in Zig?" Programming in Zig feels very Go-like, and not having closures was actually one of the biggest hiccups for me. I don't think this really changed how I write in other languages, but definitely how I think about other languages.
Snap! I also played around with closures a tonne in Zig. Definitely possible but not... ergonomic. Haven't ended up using them much.
And agree with allocators; in C I always considered using custom allocators but never really needed to. Having them just available in the zig std means I actually use them. The testing allocator is particularly useful IMO.
Never used Go but if it's Zig-like I might give it a shot! Thanks!
I'll make a list of the things that both languages have in common that make them feel similar to me:
- structs and functions are the main means of composition
- the pattern of: allocate resource, immediately defer deallocating the resource
- errors are values, handled very similarly (multiple return values vs error unions)
- built in json <-> struct support
- especially with the 0.16.0 Io changes in Zig, the concurrency story (std.Io.async[0] is equivalent to the go keyword[1], std.Io.Queue[2] is equivalent to channels[3], std.Io.select[4] is equivalent to the select keyword[5])
- batteries included but not sprawling stdlib
- git based dependencies
- built in testing
I think it mostly comes down to the standard library guiding you down this path explicitly. The C stdlib is quite outdated and is full of bad design that affects both performance and ergonomics. It certainly doesn't guide you down the path of smart design.
Zig _the language_ barely does any of the heavy lifting on this front. The allocator and io stories are both just stdlib interfaces. Really the language just exists to facilitate the great toolchain and stdlib. From my experience the stdlib seems to make all the right choices, and the only time it doesn't is when the API was quickly created to get things working, but hasn't been revisited since.
A great case study of the stdlib being almost perfect is SinglyLinkedList [1]. Many other languages implement it as a container, but Zig has opted to implement it as an intrusively embedded element. This might confuse a beginner who would expect SinglyLinkedList(T) instead, but it has implications surrounding allocation and it turns out that embedding it gives you a more powerful API. And of course all operations are defined with performance in mind. prepend is given to you since it's cheap, but if you want postpend you have to implement it yourself (it's a one liner, but clearly more expensive to the reader).
Little decisions add up to make the language feel great to use and genuinely impressive for learning new things.
C does not provide vector primitive to expose the vector primitives in modern machines. C compilers rely on analyzing loops to see when auto-vectorization is applicable. Auto-vectorization is a higher level of abstraction than directly exposing vector primitives.
Regarding the lack of a standard global allocator, and the lack of implicit buffering on standard io functions, these are simply features of the Zig standard library which are true of computers (computers do not have a standard global allocator nor do they implicitly buffer IO) but are not features of the C standard library, and therefore are not encouraged to use custom allocators or explicit buffering.
Zig also encourages you to "think like a computer" (also an explicit goal stated by Andrew) even more than C does on modern machines, given things like real vectors instead of relying on auto vectorization, the lack of a standard global allocator, and the lack of implicit buffering on standard io functions.
I would definitely put Zig on the list of languages that made me think about programming differently.