> For me, no exceptions is a show-stopper.
> For me, no exceptions is a show-stopper.
Go has exceptions, but it differs from, e.g., Java in that:
1. It handles raising and recovering from them, and specifying actions that must happen whether or not an exception occurs, through two built-in functions (panic/recover) and a simple statement (defer) rather than a multi-armed structured syntax (try/catch/finally).
2. The built-in functions and standard library use panics (Go's exceptions) with more restraint than is idiomatic in Java.
> In a typical scenario, I might be working on an utility that does some IO. In case of error, I just want to do some cleanup then exit. With exceptions, the control flow will jump right up to the places were cleanup has to be performed.
And this works fine with Go's panics. You'll be more likely to need to decide that something is panic-worthy and explicitly panic in your lowest level call in Go than in Java (just as, conversely, you'll be more likely to need to explicitly catch and handle an exception thrown in Java for something that's not really exceptional in your use case.)
But aside from that, there's not a lot of difference.
> With return values, I need to check return values at each step of the call stack, which is tedious and ugly.
Or check them at the lowest level of user code and, if there is a problem that is truly exception/panic worthy at that level, call panic().
If you are checking errors at each level of the call stack just because the lowest level function uses (as is typical in Go) a secondary return values to report conditions outside the scope of the main return value, you aren't dividing functionality up properly between functions. Generally, I would think, the lowest level of user code that is specific enough to make an intelligent decision should either be collapsing out the unusual-condition secondary return value or initiating a panic, depending on how the condition involved relates to the purpose of the code.