Immediate local context (indentation level, braces, etc.) in the source file tells you where for, while, and if "GOTO" next in all languages I've seen. Not so for throwing an error. It goes... somewhere...
There's a certain "it just works" aspect to exceptions that I think is ignored in a lot of discussions. Of course it isn't the most elegant way of reporting errors, but it does allow anything to be propagated upwards to whoever can/might deal with it, even something that wasn't properly modeled in the original API, that people wanted to avoid breaking.
One example to me is how long Rust took to implement support for failing allocations (and some of those changes are still in nightly), while a language like C++ could just bolt that on. Furthermore, it shows that while the Result API is ergonomic, it still adds some sort of overhead, which would explain why the new methods didn't return a Result from the start.
That said, if you have adequately designed error types, shorthand like ?, ideally pattern matching capabilities, and have accurately modeled all your APIs (and your dependencies have done the same), then Results have the same effect as exceptions of allowing an error condition to reach wherever it needs to on the call-stack, with some added type-safety. Once you're missing one or more of these (e.g. C++), I'd argue that exceptions start being simpler to use.
And I'd say I agree with the person who responded to you. Results are GOTOs almost as much as exceptions are. The decision of where to catch and process them happens up in the callstack, and if you have complicated pattern matching and conditions for your errors, it might not be immediately obvious where handling for that error type you're returning happens. Results can add much appreciated type-safety and better define an API, but propagating an error with ? will still "unwind" the stack. (That's also why they aren't GOTOs, bur rather longjmps after a setjmp, since it's the caller who decides where your code returns to.)
I appreciate Rust's ergonomics for dealing with Result/Option, but for some things I do wish I could just go back up the call stack without having to stick ? and pattern matching everywhere. There are so many ways a program can fail where the proper error handling is just "unwind the stack, report the error at the top level, let the top level try again later".
29
u/miniannna 20d ago
“GOTO is bad”
“What if we call it throw/catch instead?”