Live data from Hacker News

Gopher Wrangling: Effective error handling in Go

stephenn.com

1–10 of 310 posts

Re: Gopher Wrangling: Effective error handling in Go

#4
One thing I did for a project of mine was define my own error type, so that I can include some specific information for the next layer up. In short, I added information about the severity of the error so that the calling function that's capturing it can decide if it can recover from it or not based on the severity, and I added a flag to determine if the result value (think "T, error") is empty, partial, or complete.

I added .Empty() and .Partial() because if you're returning "string, error" from a function, for example, then "" doesn't cut it for me and instead of checking for "" in the calling function, I can instead check for err.Empty(). This doesn't seem like it's useful, but take that idea and apply it to two additional scenarios: a non-pointer to a struct{} with 10 fields (are they all empty?), and partial return values i.e. the function you called threw a warning and only partially populated the return value. Now the calling function can shift the "is empty" checks to the function that actually constructs the return value (or not.)

Now I can call a function, get my custom error type back, and I can determine if there was an issue and whether or not the value is empty or partial regardless of the type (and its complexity.) This paid me back in dividends the moment I wanted to be able to return a warning and a partial result - so not workflow breaking, but also not everything the caller asked for... it's up to the caller to determine if it has what it needs to continue.

Re: Gopher Wrangling: Effective error handling in Go

#5
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 grassroots patterns and efforts to handle multiple errors per function, or async errors etc all should really have better solutions in the language.

I understand that maybe the language authors in the early days didn’t want to lock anyone into a strict paradigm for how to deal with errors. Like I’m not thrilled about Java’s approach either, but that can never change. But Go is a very popular and established language now. It’s time to fix the error handling mess. There are so many good examples out there to get inspiration from. F#, Swift and Rust have a perfect error handling mechanism.

Re: Gopher Wrangling: Effective error handling in Go

#6
For a language where coroutines are such a first-class citizen, I wish there was a more idiomatic way of returning and handling async errors in Go. I know it's all over the docs, but using a channel has always felt so "wrong." The errgroup lib tries to fix this, but it's still not as flexible as using a channel (for example, if you want to store or log all routine errors).

Re: Gopher Wrangling: Effective error handling in Go

#9
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.

Re: Gopher Wrangling: Effective error handling in Go

#10

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…

> F#, Swift and Rust have a perfect error handling mechanism.

Rust's is good but not perfect. I often find myself missing stack traces (there are solutions but they're not easy to use), and you're still constrained to a single type of error per function, which means you see a proliferation of specialized error types that are mutually incompatible and have to be converted back and forth.

Post reply on HN