10 ms·
I don't think you're weird - I understand that some people like it and I can kind of see some of the arguments as to why - but the error handling in Go is easil
by suresk 5y ago
I don't think you're weird - I understand that some people like it and I can kind of see some of the arguments as to why - but the error handling in Go is easily my least favorite thing about it. For two reasons:
1) The aforementioned boilerplate code that makes up a significant chunk of many Go projects and adds no value.
2) With explicit error handling, however deep your call stack is, you are relying on everything in that stack to have done the right thing with regards to error handling. With exceptions, you have to go out of your way to screw them up.
I can't tell you how many times I've gotten dumb error messages like "Invalid string" from a 3rd party library that take forever to debug, and accidentally swallowing an error is even worse. A simple (even crappy) exception message + a stack trace is much easier for me to use for debugging than even the best handcrafted error message in 99% of cases.
I write somewhat equivalent amounts of Python, Java, and Go lately, and each one has their good and bad parts, but there are few things I dislike as much as Go's error handling patterns.
- ithkuil 5y ago> With exceptions, you have to go out of your way to screw them up. I remember well java codebases littered with: try { .. } catch() { // Todo } Usually cheaply inserted by the IDE. I'm pretty sure nowadays there are linters that will ensure you have to do some extra work to actually check in such code, but still...
- suresk 5y agoI've never had a Java IDE insert something like that? But either way, you have to go out of your way to do that, which is sort of my point. The one place where you do see dumb boilerplate and chances to screw up is in dealing with checked exceptions, which have been controversial since the very beginning. I think they are one of those things that seem like a great idea in theory, but end up not working out so well in practice, but (like people who appreciate Go's error handling model) there are people who disagree.
- m45t3r 5y agoExactly. It is kinda obvious that someone is doing something wrong with the code when they're doing a generic catch (sometimes it is fine, but this will at least raise some eyebrows). However it is very easy to do the wrong thing using Golang. Just one random library doing `fmt.Errorf` without the `%w` verb is sufficient to lost all information from there on. I would much prefer some kinda of annotation that does the correct thing by default (wrapping the error) instead of the "every error should be explicitly" approach of Golang.
- ithkuil 5y ago> I've never had a Java IDE insert something like that? yeah this indeed happens with checked exception and careless developers just wanting to get stuff to compile (we'll handle that property later) and the IDE helpfully providing the boilerplate that resolves the compilation error (and assumes you'll fill the body)
- anonymoushn 5y agoThe above code is probably caused by checked exceptions. In particular, the user wants to implement some interface or override some method, but that method wasn't declared to throw a particular type of exception because the people who wrote it hate extensibility and never want their software to be used in a way they didn't anticipate, so the new implementation can't throw it either.
- johnmaguire 5y ago> With explicit error handling, however deep your call stack is, you are relying on everything in that stack to have done the right thing with regards to error handling. With exceptions, you have to go out of your way to screw them up. I don't think I agree with this argument. In many languages, there's no obvious indicator that any given function may or may not raise / throw an exception. And even if it doesn't throw an exception today, it might tomorrow, and it's easy to forget to update all callers. Since Go makes errors a return value you have to actively discard the result (by replacing it with a underscore, e.g. `res, _ := getResults()`) or take the time to handle the error. And just like an intermediary library in Go can swallow the error, so can an intermediary library in an exception language catch and discard an exception. It seems to me that the result is more errors are properly handled in Go - because they are explicit - while uncaught exceptions often cause bugs that make it to production. For context, I recently switched jobs from one where I wrote Python for 4.5 years to a job where I've been writing Go for about 5 months.
- suresk 5y ago> With Go making errors a return value you have to actively discard the result (by replacing it with a underscore, e.g. `res, _ := getResults()`) or take the time to handle the error. Right, but because `err` ends up getting re-used in so many cases, you can re-assign it and forget to do anything about it (which is what I've found in most cases where an error was inappropriately suppressed in Go). In most cases, simply bubbling up the error to something that will generically handle all errors is the right thing to do, and in the case of exceptions, even if you don't know about one, that is what will happen. I just sampled a handful of the top Golang repos on Github, and found very few cases of anything more than the standard `if err != nil; return nil, err' pattern that just bubbles the error up.
- kortilla 5y ago> Since Go makes errors a return value you have to actively discard the result (by replacing it with a underscore, e.g. `res, _ := getResults()`) or take the time to handle the error. This is a bad thing because it’s not narrow. It’s like a language’s “catch” statement not allowing you to catch specific exceptions. Once someone has decided to throw away an error with the underscore assignment, all future errors the underlying might return are swallowed as well. In other words, it takes even more boilerplate to have narrower error exemptions than to have the generic exemption. > It seems to me that the result is more errors are properly handled in Go - because they are explicit - while uncaught exceptions often cause bugs that make it to production. An error in go that is blindly returned all of the way up the stack has no difference with an uncaught exception.
- laumars 5y agoThere’s two types or reason for a program to error: - the code has failed and the developer needs to be informed what to fix - the environment has failed and the user needs to be informed Exceptions are for the former, which Go still supports via Panic(). Errors are for the latter. Which is why all the boilerplate Err code creeps in. I don’t like getting stack traces from applications when I’m a user. It always feels to me like the application is only half finished. When the error is environmental (eg file system permissions) a stack trace just muddies the water with unnecessary output. This is why I like Go’s distinction between errors and exceptions.
- zarzavat 5y agoThis is great in theory but in reality no such distinction exists. For example, is trying to read a missing file a failure of the code, or a failure of the user? It could easily be either, the developer hardcoded the file path wrong during development, or the user selected a wrong path in a CLI, ... the distinction between errors and exceptions must made by the call site, not the definition site. The code calling readFile() knows whether the error is recoverable or not. readFile() itself does not, so the same mechanism should be used for both errors and exceptions. All this was figured out decades ago, that's why exception systems were invented, but language designers keep making the same mistake over and over again in an effort to simplify the unsimplifiable.
- atomicity 5y agoExcept that people are too lazy to use exceptions that are checked at compile time. The main benefit of Go is that the people that defined the library ecosystem actually decided to handle errors. It's the bare minimum, but it's better than just forgetting that the error exists and not documenting it either. The language is ... not the best, but the libraries tend to be more robust.
- erik_seaberg 5y agoIt’s not always laziness, in Java stream.map(f) doesn’t allow f to declare any checked exceptions. We could catch and wrap everything but that obscures the useful code without any improvement in safety.