Go got this right. If there is an error, return it to the caller. The error can be a function, and the caller can call it when they want. This returns programming to a single sequence of events.
Go got this right. If there is an error, return it to the caller. The error can be a function, and the caller can call it when they want. This returns programming to a single sequence of events.
It's never been that way. What's a page fault?
> No matter how you slice it, exceptions are still a goto under the covers.
"break" is also a goto. What's your point?
> If they are truly exceptional, you might as well call exit() too.
That's completely unacceptable for robust software. Programs can recover from almost all errors and make forward progress, usually via staging some kind of rollback. Tearing down a whole process in response to error is a ridiculous extreme that works only in a few narrow domains.
> Exceptions are a mistake, plain and simple.
Is that why almost every language ends up adopting them in some way? Even languages that start out vehemently anti-exception, like Go and Rust, end up with the full try-and-catch exception toolkit. That's because, despite protestations, exceptions are extremely useful.
Rust (and as far as I know, Go) have pretty much remained the exact same with regards to their implementations here. Go uses panic/recover a bit more liberally than Rust uses panic/catch_panic; using panic for control flow is not idiomatic, and so is not done. Since you can turn them into an abort, it's not something you can rely on when writing a library, and so I'm not aware of any significant libraries that do so. You couldn't even catch panics for a long time, and the possibility was mostly added to prevent UB with regards to extern functions.
In both language environments, the availability of a compiler option that breaks a language feature doesn't erase the existence of that language feature.
What about the built-in memory and concurrency safety?
This is a poor argument; the same could be said for almost all control flow statements: if/else, while, for, break, continue, (early)return ... they're all implemented using "goto under the covers". Moreover, lots of C programs use goto to do their error handling!
Seeing "throw" statements as gotos misses the point: you can't really create spaghetti control flow using "throw" statements. Instead, you can see them as "return on steroids".
It's true that "try-catch" makes it hard to know where your code is gonna "jump" ; but that's the point : as the "thrower", you don't have to know, and you don't /want/ to know. Think about the standard libraries for Java, C#, Python, D ... they don't have this luxury. If when using "throw", you're wondering "where's the catch", then you /probably/ have a design issue.
>This is a poor argument; the same could be said for almost all control flow statements: if/else
It's technically possible to implement if/else (and loop conditions) with unconditional jumps and offsets, but for reasonable instruction sets (that support conditional jumps) they won't be "goto under the covers."
Exceptions is following the "let it crash" philosophy from erlang, instead of broken recovery code scattered all over your code. (Crash doesn't necessarily mean a linux process in this context, more of a high level) This is a very powerful concept that allows you to think about the bigger picture such as if _anything_ unexpected happens inside this transaction, return a error to the user and continue processing the next request from the next user. Error handling should be done by layers, not by grunt work.
There are situations where I agree you might want to know how a certain function can fail, here I think javas checked exceptions strike a good balance between control vs verbosity as you just have to add a throws-clause if you don't want to handle a specific error at that point in the code.
What about coroutines...?
Go with panics moved to regular error returns doesn't seem like it would lose anything.
You can handle Segmentation Fault, but you need to know what you are doing. In Go it's the same.
Go documentation is very clear on the difference between the two, actually. I've never been confused.
(I think the panic()/recover() mechanism is intentionally crude in order to discourage people from over- or abusing it.)
My POSIX knowledge is a bit out of date, but if I remember correctly what actually happens is not guaranteed to work across all UNIXes.
Besides, signals are OS specific, while C++ exceptions are compiler specific.
what do you put in "all UNIXes" ? Xenix 1.0 ? MULTICS? Because all current unices (https://upload.wikimedia.org/wikipedia/commons/7/77/Unix_his...) would have no problem running recent GCC and C++ exceptions.
I mean, for hell's sake, C++ exceptions date back to 1990. Nowadays you can throw exceptions on damn 16-bit microcontrollers (https://en.wikipedia.org/wiki/TI_MSP430) ; I doubt there's a relevant, non legacy unix where this does not work.