I think the issue is that many languages lacked support for option types for a long time so exceptions got overused and abused. Now the pendulum has swung towards "exceptions are bad".
I think both ways of handling errors serve different use cases and it is good to have proper language support for both. I like to use exceptions for errors that are... literally exceptions. Stuff that is out of my control, being out of memory, stuff that I don't expect to happen on the happy path. When the default should be to just log the thing and crash. And if your language supports proper exceptions you get nice stack traces in your logs to see what went wrong.
On the other hand error types are superior for things that you should explicitly handle in your code. That are reasonable invariants to take care of. Here the language forcing you to handle them or to explicitly opt out of handling them is extremely valuable.
Having more tools is not always bad.
My understanding is that Zig errors can't contain any data. But an error message like "file not found" is awful. Are your error messages awful, or is there some way to get information into them so that you can say "file 'foo/bar.json' not found"?
If you need more than that and error codes, then either log as the sibling suggested or use custom return types. Anything more complex than what can be encoded as error codes is not an exception.
https://wg21.link/p2544#expected
>> the overhead is so high that std::expected is not a good general purpose replacement for traditional exceptions.
I'm guessing you don't care about performance that much.
It seems pretty correct to me? It's just based on first principles. Returning a value + error is strictly more work than returning just a value, and compilers can't solve the halting problem to optimize away arbitrary amounts of extra work. Ergo, performance hit.
I'm comparing in-band to out-of-band error signaling, not "error handling to no error handling".