> Zig doesn't have the memory safety mechanisms of C++ level shared_ptr/unique_ptr.
Zig's memory-safety is spatial, which is the more dangerous kind. Your examples are all temporal, and I don't know if I would call them "safety" as the language doesn't enforce or even encourage their use. For example, I work on one of the world's most foundational C++ projects (the HotSpot virtual machine), where neither shared_ptr nor unique_ptr are used at all; the language doesn't complain. There are reference-counted pointer libraries for Zig, too (although I don't think they're popular).
> It's essentially C's malloc/free with the added safety of defer
No, it isn't. First, unlike C or C++, it offers Rust-like spatial memory safety (again, the more important kind). Second, allocation in Zig is much more explicit than in C or C++.
> Besides, the way you trigger memory leaks in Rust/C++ is with circular references, which are much harder to create than just missing a free() somewhere.
They're also much harder to detect, especially when you have a very explicit memory allocation scheme.
> If you have memory that's not tied to a function scope's lifetime in Zig (or the scope of an allocator), then you're essentially have to go by C rules
Again, Zig doesn't seek to offer temporal memory safety, but it does offer the same level of spatial memory safety as Rust does, and while the "rules" of temporal management are similar to C, the way allocation works in Zig makes for a very different experience. An allocating function in Zig looks very different from its C counterpart.
Zig rejects the temporal memory management schemes of C, C++, and Rust - each for different reasons - and prefers a different kind. Of those, only Rust offers temporal memory safety (in the sense that it's controlled by the language) - albeit for a price - and the rest don't.