This higlights a point why I actually prefer exceptions to manually passing around error codes.<p>If any of these calls to panic() are inside libraries, by using that library, you will put yourself into a position where your application will exit without giving you any recourse to fix it.<p>I've seen too many C libraries doing this too - "mmh - I can't handle this right now - let me just call exit()" (last time this happened was inside a commerical library for reading excel files. If it was confronted with a file that was corrupt in some way, the library would call exit(), terminating my application server in the process. Thanks)<p>With exceptions, a library can just throw but I as a consumer of the library at least get a chance to handle the error somehow and who knows - I might be able to find some way to still proceed and not crash.<p>If I don't handle that exception, oh well, then I crash anyways, but that's not different from the library calling exit or panic.<p>With exceptions, I also never risk that I (or a library I use) forgets to check for an exit code and proceeds to corrupt data (or crash way later at a seemingly unrelated point).<p>When I fail to catch an exception, I my application dies.<p>When I fail to check an error code, my application might possibly corrupt data.<p>Just because implementing exceptions in compiled languages is difficult and messy (C++) and because exceptions are slow doesn't mean we shouldn't be using them. It means that we have to improve our language and OSes in order for them to work without hacks and quickly enough because, honestly, the value they provide to me is big enough to warrant a bit of research.
Here's why Go's errors are better than exceptions:<p>Multithreaded environments make exception handling next to impossible to do correctly. The stack trace only shows a single thread which is often useless without more context. Exceptions are per-thread, so handling has to be per-thread.<p>Exceptions make the control flow of your program completely obscure. Any line that calls out to another function can cause this function to exit. If you're not wrapping every function call in a try/finally, you're probably doing it wrong (at which point it's even more verbose than go's error returns).<p>Errors in Go aren't special. They're just values returned from a function. You have the whole language at your disposal to deal with them.<p>I can write this in Go, and it's perfectly safe:<p><pre><code> f, err := os.Open(filename)
if err != nil {
return fmt.Errorf("Failed to open config file: %s", err)
}
err = callFunctionThatMightErrorOut()
if err != nil {
// handle err
}
f.Close()
</code></pre>
I know that my code will ALWAYS close that file handle. To do the same thing in a language with exceptions, you'd have to do something like this:<p><pre><code> File f
try {
try {
f = os.Open(filename)
}
catch(Exception e) {
throw new Exception(string.Format("Failed to open config file: %s", e.Message))
}
try {
callFunctionThatMightErrorOut()
}
catch(Exception e) {
// handle error
}
}
finally {
if f != nil {
f.Close()
}
}
</code></pre>
Now who's verbose?<p>The reason people think Go exception handling is verbose is because they aren't really handling errors in their own code. Go makes it easy to handle errors in the right way.
This is really interesting to me because it shows what to think of the claim that explicit error handling makes go code more reliable than languages which use exceptions.<p>When using go I often get the feeling of doing something wrong. Because I know I'm supposed to "handle errors" but all I really want is the program to blow up and hand me a stack trace.
This gives me the worst of both worlds, I have to manually trigger something that approaches the unhandled exception behavior of other languages while retaining the verbosity of explicit errors returns.
Now it seems like I'm not the only person struggling with this.<p>Has anyone come up with a sane approach to that problem?
It's natural that most public code panics on error, even though it's bad practice.<p>Error return values (e.g. go) are better for building large systems where stability & predictability are important (that is, cost to maintain).<p>Exceptions are better for quickly building systems (that is, cost to build).
Maybe it would be better use the title of the submission to draw attention to exactly what is interesting here. All I can see is "lots of people use 'err' as the name of their returned error value in Go code on ohloh"
So I get it, and it makes a good point, but this could be more of a symptom than a disease.<p>When I first started programming C I also tended to just exit the program on getting a bad return code, because I'd never been taught how to properly handle errors - I'd always just replied on exceptions.<p>Only later did I start to get bitten, and take proper care to handle errors sensibly and explicitly.<p>As "go" is such a new language I wouldn't be surprised if a similar thing has happened to new programmers migrating over to it.
I like erlang's model here. Fail often and fail fast and let the supervisor of that particular node just restart the thing. Bug fixing comes later when you inspect your logs. Do errors bubble up in actors in go like they do in erlang?
Is this meant to be a negative comment on Go error handling not conforming to a single pattern, or a positive comment on Go error handling not needing to conform to a single pattern?<p>Or is it just a random code search posted on HN?
I've found in some cases Go's style of error handling can actually make for great documentation: when every third line of code is something like<p><pre><code> if err != nil{
log.Printf("This specific operation %v went
wrong because input %v was %v or was expected to be %v but
wasn't", operationName, inputName, actualInput,
expectedInput)
}
</code></pre>
Then reading the error messages gives a pretty good idea of what's supposed to be happening at that point in the code.
One goal, while writing programs, is to construct programs that operate correctly (that is conform to the specifications that we have or imagine for the finished product). Complex control flows make reasoning about correctness difficult. Consider a simple loop:<p><pre><code> while x != y
x = ...
y = ...
end while
-- (A)
</code></pre>
At point (A) we know that x == y. This allows us to reason about what happens next in the program. Having a <i>break</i> statement within the loop means that we now have a more complex condition to consider at the end of the loop.<p><pre><code> while x != y
x = ...
if x == 0: break
y = ...
end while
-- (B)
</code></pre>
At point (B) we know that x == y or x == 0.<p>Once we introduce exceptions, we can end up outside the loop without knowing anything about the state of x and y (unless we examine a lot more code, possibly code quite remote from this little loop we are working on).<p>go's requirement to laboriously check and handle every function return may make it easier to reason about x and y in our little example, but it comes at the cost of extra boilerplate error checking everywhere. This too hurts our ability to reason about the code we are writing by increasing verbosity, so I'm not sure that go's approach will work out much better than some form of exceptions mechanism.<p>However, I welcome this approach to language design, keeping the language as simple as possible, but no simpler. Even if go error handling turns out in practice to be roughly as good as C++'s approach, it will be a big win for go because go ends up smaller, simpler, and easier to learn and master. It's difficult to assemble a team of C++ programmers to work on complex systems because it is so hard to find more than a handful of real C++ experts at a time. I feel safer recommending the use of go to a typical programming team than C++.
On GitHub:<p><a href="https://github.com/search?q=%22if+err+%21%3D+nil%22+extension%3Ago" rel="nofollow">https://github.com/search?q=%22if+err+%21%3D+nil%22+extensio...</a>