8 ms·
> Go has multiple returns from functions so a very typical scenario is a function that returns something and also an error, which would be nil if everything wor
by csense 11y ago
> Go has multiple returns from functions so a very typical scenario is a function that returns something and also an error, which would be nil if everything worked okay
This is one of the problems I have with C. You basically have to remember to manually check for an error on every function call, which means (1) an algorithm with short and simple pseudocode becomes lengthy and hard to read real code, because you need to add a ton of error handling, (2) it is easy to forget to handle an error in some place. Exception-based error handling lets you delegate errors to a top-level handler without cluttering up intermediate code (i.e. an exception anywhere in webpage rendering logic should be handled by 500'ing the entire page, which exception-based error handling lets you do without cluttering intermediate functions between the function producing the error and the function handling the error with error-handling logic).
I've been thinking about learning Go, but if its only error handling mechanisms are "pack it into a return value" or "panic() which is like exit(1) in C" then it's a black mark against the language. It's still a black mark if the language has exception-like error handling but it's not considered "idiomatic Go" and the stdlib and a good chunk of third-party libs report errors in return values.
- sdegutis 11y agoSwift alone gets this right: It uses error values like in Go, but you must prefix any function which can throw with a try statement or it fails with a compile time error. That way you never forget to handle it, but it's not really an exception either.
- pcwalton 11y agoRust effectively does this too with the warn-unused-result lint (on by default) and the try! macro.
- knughit 11y agoTo understand Go, you need to stop thinking in terms of black marks and start thinking in terms of tradeoffs across a whole language.
- sagichmal 11y agoVery much this. Go was designed as a whole forest; focusing in on each feature in isolation is, near-literally, missing the forest for the trees.
- decker 11y agoI suppose that might be true, but it feels more like Go missed the known universe while focusing only on the forest.
- mentat 11y agoI'm not sure how you can seriously say that given the designers and the internal use at Google. But perhaps the universe excludes Google, they're too far ahead anyway.
- hepta 11y agoIt might be that the designers chose to focus on specific problems they found at Google, universe be damned. Maybe Go is just a good Google language.
- mentat 11y agoSo, universe does not include Google? Burn karma burn. =)
- hepta 11y agoThat's not what I meant. My reply to you needs the context of the parent comment. To "miss the known universe" was the explicit purpose of the designers. It was supposed to be simple (whether their definition is good or not is debatable). The number of users is irrelevant.
- millstone 11y agoThis is valid, just not for Go. There isn't some big underlying coherence guiding the design; it's more a bunch of one-offs. For example, you get a nice operator for appending to a string, but not a slice. A zero-valued slice is empty, a zero-valued map is nil. This isn't to say that Go is bad, it's just that its tradeoffs aren't in service of any big unifying vision. When range can only iterate over a slice, map, string, or channel, there's no forest. It's just four trees.
- decker 11y agoYeah, I found it quite bizarre as well. Also, the panic()/recover() looks more like they decided to unify assert with setjmp/longjmp rather than exceptions since catching only occurs after your function exits which is pretty much equivalent to having the entire function wrapped in a try-catch block, making it less useful.
- vetinari 11y agoWrt error codes and (2): in Go, you have pretty much ignore errors willingly (by explicitly assigning the error to _). You cannot ignore them accidentally without compiler pestering you about ignoring the return value.
- breakingcups 11y agoUnless the error is the only return value, in which case you can freely ignore the return-value without the compiler complaining.
- aleksi 11y agoGo makes it really easy to linters (lexer and parser in standard library, simple AST, etc.), so there is a robust linger for that: https://github.com/kisielk/errcheck https://github.com/kisielk/errcheck
- pcwalton 11y agoIt's just as easy in C to write that linter in the post-libclang world. (Except that it's not necessary, since all major open source compilers support the warn-unused-result attribute as a builtin, while Go requires an external tool.) Go's error model doesn't differ fundamentally from that of C. The built-in unused value error helps a lot by catching a sizable subset of cases, though; Go's designers made the right call there.
- jjnoakes 11y agoOn the other hand, exceptions are implicit control flow, which means you have to assume every single expression may throw an exception. So if you don't take extreme care to write strong exception safe code, subtle bugs will exist and are extremely hard to find. Explicit control flow is more verbose, but it is also easy to spot when the control flow passes over something important by mistake. And as pointed out elsewhere, forgetting to check a return value is a solved problem via static analysis.
- aikah 11y ago> On the other hand, exceptions are implicit control flow Go doesn't force you to deal with errors either. I see plenty of code ignoring them with retval , _ := MightYieldAnError(); And some languages that have exceptions actually force the client code to handle them, like Java AFAIK . Finally, Go "explicit control flow" didn't have to be verbose like it is, that's a design choice its authors made. Go is primitive in the sense that it uses C style code patterns , like the famous : retval = MightYieldAnError( &outError); Edit: I forgot to mention the panic/recover which ultimately means Go designers implemented some form of exceptions. That basically voids the usefulness of Go error system, and an admission that it's broken.
- jjnoakes 11y ago> Go doesn't force you to deal with errors either. > And some languages that have exceptions actually force the client code to handle them, like Java AFAIK. But this misses the point. Any error which you have to explicitly ignore is fine, because then you were aware of it at some point and explicitly ignored it, and anyone reading the code can plainly see you explicitly ignoring them. And errors which can only happen at specific points (like function return values) are also ok, because then you only have to consider error handling and cleanup at those points. But if you have neither of the above - if you have the situation where errors can spontaneously result from any evaluation of any expression with no explicit notation required - then you have a poor situation indeed.
- iofj 11y agoGo has both error codes, and exceptions (panic/recover). Basic errors implicitly panic, such as null pointer exceptions, arithmetic errors, ... So in Go you have the worst of both worlds. Constant error handling code everywhere, AND you have to treat every single expression as if it may suddenly throw panics. Oh and to make matters even worse, Go has typed nil, which is not equal to untyped nil. So you cannot correctly check interfaces against nil, the most common cause of panics in my experience. // t is some kind of interface if (t == nil) { // t is untyped nil } else { // t is non-nil OR a typed nil } I'm not even sure what the correct fix is. Should every interface come with a isValid() method to check if it's usable ?
- technion 11y agoErlang packs returns into a return value, but gives you an interesting alternative for handling it: Ret, ok = Something(things), There's no need for an "if" here, you've told it to handle a failure.