Live data from Hacker News

Gopher Wrangling: Effective error handling in Go

stephenn.com

21–30 of 310 posts

Re: Gopher Wrangling: Effective error handling in Go

#21

There is a lot that I like about Go. Error handling is not one of them. On one hand, I appreciate the simplicity of it all. Nothing special about an error, it’s just part of how you do everything else. But on the other hand, there is something clearly special about an error. It’s something 100% of go users have to deal with in almost every single function call. There is something clearly special about it. These grass…

> It’s something 100% of go users have to deal with in almost every single function call. There is something clearly special about it.

I can't help but feel there is an even greater generalization here. The error problem you speak of actually applies to every type. To zoom in on errors alone may be missing the forest for the trees. It seems what is special is the need to handle values returned by a function, which includes, but is not limited to, error values. It is something 100% of Go users will have to do almost every time they call a function. Even those which do not return errors.

> Swift and Rust have a perfect error handling mechanism.

Within their respective languages they may be a good fit, but those languages are producer centric. Go is consumer centric. That leaves an impedance mismatch. I do think there is something better out there for Go, but I'm not sure that is where we are going to find it.

Re: Gopher Wrangling: Effective error handling in Go

#23
post #17

I've mostly evolved to making err a named return parameter, and inverting the err != nil check. For example: func foo() (err error) { var x any if x, err = bar(); err == nil { err = baz(x) } if err == nil { err = bat() } if err != nil { err = fmt.Errorf("%w doing foo ", err) } return } This feels somewhat cleaner to me, in particular by combining error handling (in this case just a simple wrap) in a single place at t…

Curious why you're using fmt.Fprintf and not fmt.Errorf? Or is that a typo?

And I think you're going to have problems with this pattern if you join a team using Go in an organisation. The `if err != nil` pattern is the norm, and everyone's used to it (and the regular cadence of Go code; "do the thing, check the error, do the thing, check the error" is very readable).

Re: Gopher Wrangling: Effective error handling in Go

#24
post #17

I've mostly evolved to making err a named return parameter, and inverting the err != nil check. For example: func foo() (err error) { var x any if x, err = bar(); err == nil { err = baz(x) } if err == nil { err = bat() } if err != nil { err = fmt.Errorf("%w doing foo ", err) } return } This feels somewhat cleaner to me, in particular by combining error handling (in this case just a simple wrap) in a single place at t…

please don't do this

it obfuscates the control flow, specifically the value that is actually returned

early returns on errors are good, not bad

edit you want

    func foo() error {
        x, err := bar()
        if err != nil {
          return fmt.Errorf("bar: %w", err)
        }
        
        if err := baz(x); err != nil {
          return fmt.Errorf("baz: %w", err)
        }
        
        if err := bat(); err != nil {
          return fmt.Errorf("bat: %w", err)
        }
        
        return nil
    }

Re: Gopher Wrangling: Effective error handling in Go

#25
post #19
post #11

Earlier quoted context omitted.

One thing I don't get (and would honestly appreciate if was explained to me) is how the Result monad differs significantly from Go's error handling, other than being a "true" monad. Most Rust code I see does things like (from the docs): let greeting_file_result = File::open("hello.txt"); let greeting_file = match greeting_file_result { Ok(file) => file, Err(error) => // handle err }; It isn't much different from: fil…

Result is better because it actually encodes the correct situation. You either get a file or an error. Not neither, not both. Go's encodes instead "you may or may not have a file" and "you may or may not have an error". Not the same thing, and extremely rarely what you want, IME. Other languages also do a better job of helping you verify that you actually handled both cases too. By the way I wouldn't say we need Mona…

This seems like a distinction without a difference. In reality, in code, I'm still doing something like `if (!result.failed) { do the thing } else { do the other thing }`

Like...this makes no difference to my ergonomics at all. In many cases it's arguably worse because now 1 token is potentially representing two very different types I want to deal with.

The primary benefit to me seems like it's more to do with the ease of generic code handling: everything can be Results, and then I can evaluate them all to see if any of them are errors, which in turn makes failing out of many different operations easier - I'm not handling a file type, a string, some numbers etc.

Re: Gopher Wrangling: Effective error handling in Go

#27

Go error "handling" blocks don't seem like error handling, when it's 3+ visual polluting LOC that just return the error up the call stack, occasionally with context like tip #4 of the blogpost. I've tried to like go's verbose error handling (follow the “happy path”) but the error handling signal to noise ratio is skewed in a way that makes developing in go feel slow and boring.

the idea that error handling "pollutes" code is a misunderstanding which go addresses

the "sad path" of error handling is equally as important as the "happy path"

Re: Gopher Wrangling: Effective error handling in Go

#28

"Always handle errors" sounds good until you remember that every read or write can potentially fail. My go programs are littered with unchecked fmt.Printf or Println statements.

fmt.Printf failures are un-actionable, so there's no reason to handle them

Re: Gopher Wrangling: Effective error handling in Go

#29
post #25
post #19

Earlier quoted context omitted.

Result is better because it actually encodes the correct situation. You either get a file or an error. Not neither, not both. Go's encodes instead "you may or may not have a file" and "you may or may not have an error". Not the same thing, and extremely rarely what you want, IME. Other languages also do a better job of helping you verify that you actually handled both cases too. By the way I wouldn't say we need Mona…

This seems like a distinction without a difference. In reality, in code, I'm still doing something like `if (!result.failed) { do the thing } else { do the other thing }` Like...this makes no difference to my ergonomics at all. In many cases it's arguably worse because now 1 token is potentially representing two very different types I want to deal with. The primary benefit to me seems like it's more to do with the ea…

In rust the type system enforces you check the error. In go, it doesn't. (Because rust error types are enums/sums and go error types are structs/products). This seems like a huge difference.

> In many cases it's arguably worse because now 1 token is potentially representing two very different types I want to deal with.

Yes, that's what an enum/sum type is? That's the whole point.

Re: Gopher Wrangling: Effective error handling in Go

#30

There is a lot that I like about Go. Error handling is not one of them. On one hand, I appreciate the simplicity of it all. Nothing special about an error, it’s just part of how you do everything else. But on the other hand, there is something clearly special about an error. It’s something 100% of go users have to deal with in almost every single function call. There is something clearly special about it. These grass…

if you write a line of code that can fail, then you should deal with the possibility of that line failing there, directly, in-line

_how_ you deal with that failure is a separate question

but it's critical that every fallible expression explicitly and visibly demonstrates the possibility of failure

this is in no way an "error handling mess" -- on the contrary, it is basically the only way to produce robust and reliable software at scale

Post reply on HN