5 ms·
The error handling one is an area I've never understood - in a well-designed Go app errors will be returned from functions/methods and you'll either deal with t
by nkozyra 12y ago
The error handling one is an area I've never understood - in a well-designed Go app errors will be returned from functions/methods and you'll either deal with them or not _/err.
I love try/catch/finally but I fail to see how doing either a _ or writing a simple log function to do something with returned errors necessarily represents "more code" than handling exceptions.
- drdaeman 12y agoThat creates some mostly unnecessary boilerplate. I.e., if an error happens down the stack in function to read config, which calls a function to parse a file, which calls a function to read a file, you'll have to either propagate (or otherwise handle) that `err` manually or lose the precious information. On the contrary, Java-like pattern of functions like `Config readConfig() throws IOError, ParseError` simplifies code quite a bit. No need to deal with IOError in parseConfig - if an exception happens there it'll be propagated automatically. And `throws` clause allows for static checking and warning whenever you forgot to handle something. And sane code analysis tools would also warn you whenever you really wanted overly-broad `catch Exception(e)`, too. There's a panic/recover in Go, but they aren't serious. At least, to my limited knowledge of Go, there are no guidelines on using them properly, so everyone panics with whatever they fancy, and this lack of conventions is a bit problematic, like `raise "Failed to open file"` in Python.
- RBerenguel 12y agoThe standard is usually if err != nil { return (whatever null type), err } The error will propagate up, since all calls to a function returning err should be checking for the err. In a not-so-ideal world, just return err up. In a relatively more sane world, you can send "up" extra information along the error (just creating a new error with extra info) to know the error path. The first caller will/should handle this error then.
- moreentropy 12y agoOTOH, if you follow Go's idioms and handle every error where it happens, your code will look like having a lot of boilerplate at first, but you end up with better error handling. It's the same as putting a try-except catchall around every single call in python, because in python you can never be sure (without reading the source) what kind of exceptions something will throw. For request based services and something where some big ass operation will either fail in some way (and i don't care where exactly) or be successful, exceptions are fine. If something goes wrong somewhere, log it and reply with HTTP 500. But for servers with data i care about, i most likely want to recover right where the error happened, and actively decide if i want to abort and push the error to the next layer. Not having exceptions makes this more intuitive.
- pekk 12y agoYou shouldn't be putting a try-except catchall around every single call in Python. Exceptions mean you don't have to do that.
- moreentropy 12y agoFor me the most important thing exceptions mean is that they might pop up at any time, with unpredictable types. So as long as I don't have a catch all exception handler wrapped around all code, it might crash at some point.
- TheLoneWolfling 12y agoAssuming you're talking about unchecked exceptions, correct.
- moreentropy 12y agoPython doesn't have checked exceptions.
- moreentropy 12y agoThis one really deserved downvoting, great job whoever did that :/
- bsdetector 12y ago> if you ... handle every error where it happens This is what the debate is about: "if". The problem with Go's error handling is that people don't handle every error. And due to poor documentation structure and returning one error type, often you have to even go digging through source code to just find out what the specific error values can be and under what conditions they occur, which makes it easy to write code that you think handles all error conditions but in fact does not. The code on the golang web site didn't even check the error code from println and you never see this done in code. If you point this out you're met with "why would you want to check that error?", which is a tacit admission that Go programs will always have missing error handling and that "if" in "if you handle every error" is never met in reality. In contrast to this there are no Java programs that don't IOException from a failed println, and yes even println can matter. Redirecting output with ">&-" is different from ">/dev/null".
- zimbatm 12y agoThe readConfig example is not really an issue in go. Just return the io error and type switch after the readConfig. if config, err := readConfig(ioReader); err != nil { switch err.(type) { case io.ErrClosedPipe: // do one thing case ParseError: // do something else case ... } }
- drdaeman 12y ago> Just return the io error Manually, on every function call that may fail - that was my only point why exceptions may result in a clearer code. section, err := parseSection(ioReader) if err != nil { return nil, err } Has some boilerplate, as compared to a typical exception-based section = parseSection(reader) // throws IOError
- zimbatm 12y agoYeah I get what you mean. If you forget that "throws" comment then the Java code is opaque to the exception. I think Go developer see a value in having errors exposed explicitly, it forces them to think about how to handle them instead of having a catch-all at the top of the program. But now we're down to philosophy and personal taste :)
- Iftheshoefits 12y agoI don't consider error handling to be unnecessary, but it is boilerplate, no matter how or where it's implemented. That's because every non-trivial program has the potential to enter an error state. That state can either be ignored (deliberately or not, the latter case being a major source of most of the angst and misconceptions around C and C++) if the language allows it or handled in some way, and that way is by boilerplate "if/else" or "try/catch".
- dragonwriter 12y ago> There's a panic/recover in Go, but they aren't serious. They are very serious. > At least, to my limited knowledge of Go, there are no guidelines on using them properly, so everyone panics with whatever they fancy, and this lack of conventions is a bit problematic, like `raise "Failed to open file"` in Python. The convention is that you don't let panics escape across the public API of your function -- which also makes most other cross-project conventions for panics unnecessary. Its probably useful to have internal conventions within a project for internal consistency, but since none of your panics should ever escape across a public API boundary, a broader convention would have limited benefit.
- dilap 12y agoPeople complain about the error handling of Go a lot, but I've recently switched from writing Python to Go, and so far, I am really enjoying the change to more explicit error handling. The thing is, if you're playing fast and loose, exceptions are great; there's no extra work, and it blows up if something goes wrong, so you find out -- great! But if you're trying to do the best possible thing in every scenario, it becomes much harder. (E.g. maybe for some reason a field in a record you're loading isn't valid UTF8. Let's say it's in the context of a larger request -- you still want the request as a whole to succeed; the non-valid UTF8 isn't a critical issue, just something you should log and fix later. In Python land, it's quite possible you wouldn't even realize that an exception could be thrown there. You leave your code by a totally unexpected exit. Users are seeing errors for no particularly-great reason. So: you fix things to handle this case properly, and you end up with ugly try:catch: indentation that's worse than just checking error return codes in the first...)
- seanmcdirmid 12y agoGeneric methods is one place where non-explicit error handling is essential. What if your map/select call fails? You have to make sure to thread error handling through everything that would ever take another function as an argument (since the error handling characteristics of the unknown function are open), which, in this day and age, is ridiculous. C# couldn't do LINQ at all in this case. Sometimes a bit of dynamic scoping can go a long way.
- dragonwriter 12y ago> Generic methods is one place where non-explicit error handling is essential. And, really, the error-handling part fits fairly well with Go (not only what the language supports -- which is, after all, full-featured exceptions with a different [IMO, improved] syntax -- but also with the conventions on their use across public API, though that requires some thought about the motivation for that rule and how it applies to that kind of function.) OTOH, Go's type system, as opposed to its error-handling approach, is a real problem for generic functions. Though if you just mean non-generic higher-level functions, this is less of an issue. > You have to make sure to thread error handling through everything that would ever take another function as an argument (since the error handling characteristics of the unknown function are open) No, you just have to state as an assumption of the "generic" that any function it works on is wrapped to panic in the event of error, and the caller needs to handle the panics. To fit with the Go convention on panics and public APIs, you should wrap any function that results from applying such a higher-level function to a function that an fail into another function that recovers from panics and provides error returns before passing the resulting function across a public API boundary.