Anecdotally, I really enjoy C (and Python and my Forth implementations and a few things), and Zig is 10x better for the sorts of things I like about C (some of the syntax, especially the implementation of explicit casts, is a bit verbose for my taste, but it's a small price to pay). Talking about UB and memory safety though:
- The happy path in Zig encourages memory-safe programming and has some tools (runtime checks by default, "Release-Safe" builds with those same checks) for validating reasonable behaviors.
- The (explicitly cultivated) culture around constructs like Allocators helps one be very aware of the memory they're using and what they need to free.
- Conventions of the form `try Obj.init()` and `obj.deinit()` act a lot like RAII and further emphasize the happy path in ziggish code.
- Directly contrasting the rust I write for $WORK and the zig I write for me, especially when the actual memory layout matters (e.g., pick your favorite performance metric) I don't have a substantial difference in the memory safety bugs I write in either language. Rust is actively trying to improve that story and reduce the need for unsafe blocks for atypical constraints, but IMO it's not good enough yet.
- Zig has some shocking footguns that combined with the current pre-1.0 state of code and documentation can introduce issues you probably would not expect. E.g., most meaningful uses of async frames require them to be stored somewhere, but applying the same coding techniques you would in most Zig code and adapting the docs to do so you'll write something that creates a frame and puts it somewhere. Doing so will actually work for small examples, but as soon as that frame does something with a pointer to anything in its stack it'll blow up in production because it's retaining a pointer to the old memory you copied from rather than the new place you copied it to. There are easy solutions, but without in-depth knowledge about exactly what's happening (which is hard to obtain from the current docs) it's hard to realize you would have to find a solution (admittedly, the Zig Discord is __amazing__ at quickly helping people with such issues, so it's not necessarily a huge deal since it's solvable that way now and probably solvable with better docs post-1.0).
- I've only said anything about single-threaded memory safety. Rusts compile-time reader-writer locks are next-level when working on nitty gritty concurrent code. I've hacked together some interesting parallel Zig projects, and I'm like 99.9% sure they're correct now, but it was an adventure to even get it that far, and I'm still not 100%. I haven't been actively following Zig news for a couple months, but last I heard they're planning something important with async/await to improve this story, but it's not there yet.