It isn't that much more work and it's not something you end up doing terribly often. I think destructors are a much more important to help day to day development and avoid bugs.
That doesn't mean Zig couldn't do something similar if they want to, just pointing out that it's not always the case that it means dynamic dispatch.
Okay, you can solve the forget problem with static analysis. But since in most cases I don't care about destruction so long as it happens, when reading the code I prefer this hidden.
Abstraction means hiding details and done right is a great thing. Yes abstraction often is abused to hide the wrong thing, but that isn't the fault of abstraction as a concept.
withThing { thing => doStuffWith(thing) }
This makes it clear to me that the object is only valid within this block. There is no copy constructor madness where I might completely lose track of where the object might be alive and in scope.Objects also won't be silently copied unless they are types that explicitly marked as trivial to copy (i.e. bytewise).
It then also guarantees cleanup if the object actually does exist.
Rust already does what many people want for deterministic, safe cleanup, which like C++ includes RAII, but unlike C++ is not limited to RAII.
But you might want to care about where in the code the destruction happens. This is quite normal in a low level language. Of course I do agree that it should be easy to avoid leaking resources, but static analysis can ensure this in most cases as you point out.
If the new variable has a smaller scope, then the object will be destroyed sooner than it would have been otherwise. In particular, to assist in early cleanup, the standard library has a drop(value) function, that takes ownership of any value and immediately lets it fall out of scope, destroying it.
This is especially useful for things like destroying RAII mutex guards to immediately unlock the corresponding mutex.
If you're writing the kind of things where you don't care at all about destruction so long as it happens, why even consider something as low-level as Zig? There are many other languages already covering that niche.
As an aside, solely for the purpose of making Zig usable as an environment for people whose primary professional responsibility is not really software (quantitative researchers, data scientists, etc.) I would prefer if RAII was somehow very painful to implement for a type rather than impossible. That's the status quo for dynamic dispatch.
If Zig really wanted to do the best job here, it should recognise that we're semantically not using this 4th padding dimension if we're not doing 4D projective stuff. If it really is going to force you up to 4D space, then it should be doing compile time checks e.g. sorry, you tried to add point plus a point which makes no sense, unlike vectors, or vectors and points.
This might become more important if Zig to GPU code translation becomes a thing...
I'm sure Zig has its use cases. For what I write, I not only don't care if there's a hidden function call or error handling, I see those as 100% necessary for a modern language.
Needing to handle errors inline is a huge mess for anything nontrivial. It distracts from the logic that's important at that point in the code. Being able to override an accessor to do something instead of being a raw access is incredibly useful; a tiny change and rebuild is all that's required to track information that you would otherwise need to rewrite an entire app to support.
If you're writing extremely low level code and libraries, especially embedded, then fine, minimizing hidden behavior is important. Being able to operate without a standard library is also important in that case. Outside of that niche, though, there are few places I'd call those "features" of Zig an advantage.
Maybe try zig first before you make such a blanket statement?