7 ms·
The lack of exceptions just seems like a total bear to me. I wrote C for 10 years and did tons of stuff like: Open file "foobar.dat". If that fails, abort
by graue 13y ago
The lack of exceptions just seems like a total bear to me. I wrote C for 10 years and did tons of stuff like:
Open file "foobar.dat". If that fails, abort with this error.
Read the first 4 bytes into x.
If that fails, abort with a different error.
Seek to byte x. If that fails, abort with yet another error.
and so on, and so on, over and over again. Python's exceptions are a huge improvement over this pattern of timidly asking permission to do anything. The fact is there are so, so many occasions where you want to abort a computation if one of the many steps along the way goes wrong. Something like Haskell's Maybe monad is a good way of attacking the problem too.
But Go has neither. It seems to just offer the bad old clumsy C way and say, "Deal with it." To those who have written real Go programs, I'm honestly wondering: how is this not a pain in the ass?
- orangethirty 13y agoI'm writing a pretty big system in Go, and had not seen this as much of an issue. Could you provide more insight into your point? I'd like to see some code to compare, if its not to much to ask.
- graue 13y agoI'm not sure how great of an example it is, but I had the thought when recently rereading this routine from an old game, reading its level file: static void scanlevel(int num, FILE *fp) { filemap_t filemap; int i; if (fseek(fp, mapptrs[num-1], SEEK_SET)) error("Can't load level %d from blockman.lvl", num); if (fread(&filemap, sizeof (filemap_t), 1, fp) < 1) error("Can't load level %d from blockman.lvl", num); if (filemap.startx < 0 || filemap.startx >= LEVWIDTH || filemap.starty < 0 || filemap.starty >= LEVHEIGHT) { error("Level %d is corrupt", num); } map.startx = filemap.startx; map.starty = filemap.starty; for (i = 0; i < LEVWIDTH*LEVHEIGHT; i++) { map.tiles[i] = (tiletype_t)filemap.tiles[i]; if (map.tiles[i] < 0 || map.tiles[i] >= NUMTILES) error("Level %d is corrupt", num); } } This shows another benefit of exceptions, which is that if uncaught, they stop the program with a traceback of the exact point they occurred. So it's not even necessary to write most of the checks above. `error` here is a routine that aborts the program; you get that behavior by default with exceptions. Whereas in C/Go if I forgot one of those error checks, the error would occur silently, leaving the program in some weird inconsistent state that I never planned for. It would just do something stupid and maybe crash or panic later on, far from the place where the initial error occurred. I guess I'm just arguing for exceptions, which is old news as languages that have them have been around for quite a while. But Go doesn't offer much of a substitute of which I'm aware. The explanation of how it solves these problems has not been forthcoming.
- zackelan 13y agoI think the key difference is between assertions/"this should never happen" error checking, and actual error conditions that you want to pass back to client code, because it knows better than you do what the right thing to do is. When you're writing reusable library code (and when you think about the scale of Google's codebase, they must have an insane amount of these libraries), it's important to make this distinction. There are some error conditions where you really just want to say "if this ever happens, just die, because there's nothing sane to be done", and Go provides panic() for these situations, similar to the error() function in your code above. For situations where you do want to return a meaningful error to the client, I think Go's multiple return values provide a very good way to do it, far better than the overloading of NULL or -1 that you find in C and C++.
- pacala 13y agoDoes panic isolate gracefully a query-of-death, or does it take the entire server down? Does panic provide the stack-trace?
- pjmlp 13y agoIf you don't use recover it takes the server down. http://blog.golang.org/2010/08/defer-panic-and-recover.html http://blog.golang.org/2010/08/defer-panic-and-recover.html http://code.google.com/p/go-wiki/wiki/PanicAndRecover http://code.google.com/p/go-wiki/wiki/PanicAndRecover
- EdiX 13y agoA panic unrolls the stack until it's recovered from. If it's not recovered the entire server will go down. If the panic isn't recovered it will print a stack trace, if it is recovered you can get a stack trace with runtime.Caller(). Go's panics are exceptions.
- pacala 13y agoThanks!
- MetaCosm 13y agoUnlike C, Go isn't overloading what is returned, it has an extra param, and the language has been baked to handle doing like initialization and checks in single line if statements. It does force the developer who might actually have a clue what the exception is and how to fix it to handle it, but IMHO this is a good thing. Go forces lots of things like this (not using an import, won't compile; not using a variable, won't compile). Honestly, compared to the exception hellscapes I have had to deal with in Java and C++ --- it seems like the path of least surprise. Which incidentally has been my favorite things about Go, the low number of surprises. A lot of using Go in real work has gone against my expectations. There are a lot of things I initially saw as huge warts (lack of exceptions, generics and import versions), but I liked channels enough (Erlang background) to give it a shot. So far, I have been delighted by using it as a stack (build cycle, deploy method, terse C'ish syntax).
- TylerE 13y agoTo make one correction, it isn't an "extra param", go has full support for multiple return values, full stop.
- throwit1979 13y agoThe problem with this "forcing" that go does is that it ALSO includes _, which means it's inevitable that lazy developers will get tired of handling error and just shunt it into _.
- nknighthb 13y agoYou can't stop people from being lazy. Look at all the Java and Python code that forcefully ignores exceptions. It is something that can be caught with static analysis, however. Someone recently put together an appropriate tool[1] for Go, in fact. It seems to work very well. [1]https://github.com/kisielk/errcheck https://github.com/kisielk/errcheck
- pcwalton 13y agoTo be fair, the ability to use static analysis for error code checking is not something that is unique to Go. There was a paper recently on doing this for C (which found hundreds of bugs in the Linux kernel due to incorrect error code handling): http://pages.cs.wisc.edu/~liblit/ghc-2011/ghc-2011.pdf http://pages.cs.wisc.edu/~liblit/ghc-2011/ghc-2011.pdf (Incidentally, the hard part of this analysis is not verifying that you checked the error, it's verifying that you propagated the error codes properly—that requires analyzing higher-order control flow.)
- zik 13y agoWriting exception-safe code is non-trivial. There has been a lot of debate recently about whether exceptions are a mis-feature. The main worry is that if you allow exceptions sensible-looking code can lead to many non-obvious bugs. This link gives some insight: http://stackoverflow.com/questions/1853243/c-do-you-really-write-exception-safe-code/1853769#1853769 http://stackoverflow.com/questions/1853243/c-do-you-really-w... Some more reasons why exceptions are problematic: http://mortoray.com/2012/04/02/everything-wrong-with-exceptions/ http://mortoray.com/2012/04/02/everything-wrong-with-excepti... Go's choice I think is very well considered. If reliability is important to you then I believe handling errors explicitly leads to clearer, more correct code.
- quatrevingts 13y agoThese criticisms of exception handling are primarily based on their implementation in C++; they are not issues inherent in exceptions. Exceptions are clearly more problematic when they are bolted-on after the fact to a language with manual memory management and a large existing body of code that is unaware of them. But writing exception-safe code IS trivial, in basically any language except C++. Garbage collection is all you need in most cases, and try-with-resources/RAII/etc takes care of closing I/O. Note that Go still needs defer() to reliably take care of the latter, so it's not clear to me what is gained by the omission (or rather, strong deprecation) of exceptions.