I share your view partially. As you said, code generation is difficult and it can get messy pretty fast, but this is the beauty of Zig, you have these features tightly packed, there is a capped number of ways for doing things. In Zig you learn how to use the wand properly, instead of being distracted by trying different sizes and colors of wands. While many languages focus on the breadth of ways of doing things, Zig is all about the depth of a small set.
Sounds a lot like Go. For me, that was a huge problem with Go.
I can only speak for Go, not Zig at all, but not giving me the "wand colors" meant that when i still needed colors to solve my problems, i had to invent them myself. .. okay this analogy breaks down there, but yea. The need for basic things like iterators, enums, and sometimes even generics didn't go away in Go. They didn't stop being extremely useful patterns or abstractions. They're just missing.
So what do you do with something that is still useful, possibly needed, but missing? You reinvent it. Very basic behavior like Iterators, Maps, etc become separated by piles of functions spread out all over the page. Yea, it's all simple - no complex features, but also no way to express that logic tightly, quick to reason about. Go wears down your scroll wheel in my experience (~5 years).
Would i have the same complaints about Zig? Your comment leaves me feeling like i would.
> So what do you do with something that is still useful, possibly needed, but missing? You reinvent it.
Yup, see for example java.lang.Comparable which is basically just the standard library going "yeah the language screwed up, here's your operator overloading"
Back then the main draw of Python was supposed to be the "one way of doing things" - Python originally started as a teaching language.
And look at Python today - there's what, five different "standard" ways of packaging libraries? (And why is "packaging libraries" even a thing?) Instead of "batteries included" we get at least four different ways of doing every common task: the stdlib Python 2 way, the stdlib Python 3 way, the "standard" community third-party synchronous library and the "standard" community async one.
This is just how it always is. Every language starts with the goal of being small, easy to understand and beautifully composeable.
The cruft builds over time because people eventually want it and because none of it ever really goes away due to backwards compatibility.
I think it's best to make peace with this fact, learn to live with the cruft and accept existing languages rather than switching your entire stack every three years trying to chase an unobtainable dragon.