4 ms·
I don't know. I find the distinction of "panic if it's a bug, return err if it's a normal condition" to work pretty well. It's much easier for me to write robu
by dilap 8y ago
I don't know. I find the distinction of "panic if it's a bug, return err if it's a normal condition" to work pretty well.
It's much easier for me to write robust server software in Go than in either Java or Python -- because I don't constantly have to be thinking, "oh, is this thing going to suddenly unexpectedly throw?"
Basically, exceptions for exceptional conditions is good. But Java and Python use exceptions for normal conditions, which is cumbersome and annoying.
So for me, Go's approach to error handling is an improvement over just exceptions. The fact that it's verbose is fine if maybe not ideal.
(My vague understanding (just from reading, not using) is that's rusts is similar but more ergonomic. Ditto for Zig.)
- atombender 8y agoThe problem in Go isn't that the semantics are wrong, it's that the error handling is so prevalent that it starts obscures your logic. I'm writing an app right now where almost every piece of high-level code is: if err := doThis(); err != nil { return err } if err := doThat(); err != nil { return err } if err := doThisOtherThing(); err != nil { return err } // ... The patterns in Go code are typically either (1) "call this and bail if error" or (2) "call this and bail if error, wrapping the error in a new error", with a sprinkling of (3) "if the error is io.EOF or whatever, do something else". The #1 case is so dominant that Go really cries out for a built-in error-propagation mechanism. The other problem with Go's error handling is the use of multi-value return types. I suspect Go's designers thought this was elegant, but it confuses things, because all error-returning functions return either a value or an error. Never both. It's not two return values in actuality, it's a sum type (aka product type or union). I'd take this over the current scheme: func GetFile() (io.Reader | error) { ... } And then maybe: switch t := GetFile().(type) { case io.Reader: ... case error: return err } which can have the following semantic sugar: r := try GetFile() Rust has a macro that does something like the above, but the type system supports sum types and matching already. Now you can do: r := try GetFile() count := try r.Read(buf) instead of: r, err := GetFile() if err != nil { return err } count, err := r.Read(buf) if err != nil { return err }
- dilap 8y agoagreed; there's real (potential) niceness in these ideas.