It's poignant to watch Go slowly realize the why other languages have more powerful, more useful error types. The `error` interface is anemic to the point of uselessness. Making it unwrappable is step forward, but all that does is progress it the level of 1995 Java with their "cause" field. It still can't express suppressed errors (like those from `defer f.Close()`). There is no standard mechanism to include an optional stack trace. Worst of all, rolling your own error type and trying to use it everywhere is near impossible. Every method signature has to use `error` due to lack of covariant return types, and heaven help you if you return a nil struct pointer, which will be auto promoted to a non-nil interface. Perhaps in Go 3 they'll get it right.
Stack traces are desperately needed to give errors context, and I was very disappointed to see that proposal dropped from Go 1.13. At the same time, I'm hesitant to use xerrors in production code because it's likely to change rapidly in the future.<p>I had a situation recently where a fairly complicated API was returning an HTTP 403 in production. Once the error bubbled down to a level where it was actually logged, all that was left was "403 Forbidden". I spent days echo debugging and could not reproduce the problem. Finally, by chance, I happened on the problem while working on something completely unrelated. With a stack trace, 3 days of work would have taken 5 minutes. Wrapping would not help in this situation unless every single error was wrapped with a unique string, which frankly is ridiculous.
I love Go, but error handling feels like hand-carried, checked exceptions. And now they figured adding a “cause” field is practical. I’m getting deja-vu with Java exceptions 15-20 years ago.
The way Go handled errors is why I made the decision to go with rust as my hobby/personal-open-source-projects language. It was just extremely tedious, despite Go being nice overall.<p>I love how error handling is done in Rust. Yes, implementing the From<T> trait can get verbose, but wrapper libraries like Failure or Snafu exist now as well.<p>Being able to propagate errors down the line seamlessly with the ? operator and Result<T, E> type is nothing short of phenomenal. It looks like the next version of Rust will seamlessly allow using ? on None types as well.
<i>Go’s treatment of errors as values has served us well over the last decade.</i><p>Has it? As a Java developer who needed to write something in Go, this has been quite a frustration for me, personally. In Java, when I get a 100-line stacktrace error, my IDE analyzes it for me and I can click through the code and follow what's happening. In Go, I get a string from the developer. Hmm...
Oh I wish Golang had something similar to Rust's Options and Results. This `nil` value is the source of many crashes in applications I've audited (due to applications forgetting to verify if the value is `nil` before acting on it).<p>Maybe something like this could be supported natively in Golang:<p><pre><code> func thing(arg bool) Error.Result(bool, error) {
if arg {
return Error.Ok(false)
}
return Error.Error(fmt.Errof("nope"))
}
func main() {
if thing() == true {} // doesn't compile
match thing(false) {
Error.Ok(value) => {
fmt.Println(value)
},
Error.Error(err) => {
log.Panic(err)
}
}
}
</code></pre>
just for fun I wrote an ugly PoC for bool options: <a href="https://github.com/mimoo/Bool" rel="nofollow">https://github.com/mimoo/Bool</a>
Can someone help me with what seems to be a basic problem? How are you supposed to get stack traces for errors in Go? You can wrap an error every time you return one, but this omits useful information like the line numbers. Not to mention, a significant amount of the code you are writing is going only to providing helpful error messages.<p>Alternatively, it looks like there are libraries out there[0] that will include stack traces for you. It seems weird to me that it's necessary to use an external library to get what seems like it should be built into the language.<p>Can anyone enlighten me?<p>Edit: Forgot to include the reference:<p>[0] <a href="https://github.com/pkg/errors" rel="nofollow">https://github.com/pkg/errors</a>
Great to see this getting standardized!<p>I maintain an error package that lets users use structured types for errors and error codes [1]. It is critical to be able to wrap errors without information loss. I used one standard, a Causer interface, but I will be switching to Unwrap.<p>I see complaints about stacktraces here in the comments. I recommend always using pkg/errors or some error package with stack traces.<p>[1] <a href="https://github.com/pingcap/errcode" rel="nofollow">https://github.com/pingcap/errcode</a>
After years of using Go, I came to realize that the ONLY people that complain about errors in Go are those who actually do not write any Go code and those who write Go code less than a year(ie. juniors). There is absolutely nothing wrong with errors in Go and the creators got it right form the start. Nobody is forcing you to use errors. You can use booleans if you want. You can panic(throw exception) if that is what you wish. To each its own. If you want more syntactic sugar, move to Java, it has plenty of it for you.
Almost every language supporting inheritance suffers from lack of ability to check if an error is part of the derivation chain.
I have implemented this using expensive dynamic cast in C++ and instanceof in Java.<p>Finally Go gets it ahead of the other languages!
Check the original proposal [1] and discussion [2] as well as the unofficial package [3][4].<p>[1] <a href="https://go.googlesource.com/proposal/+/master/design/29934-error-values.md" rel="nofollow">https://go.googlesource.com/proposal/+/master/design/29934-e...</a><p>[2] <a href="https://github.com/golang/go/issues/29934" rel="nofollow">https://github.com/golang/go/issues/29934</a><p>[3] <a href="https://github.com/pkg/errors" rel="nofollow">https://github.com/pkg/errors</a><p>[4] <a href="https://github.com/pkg/errors/tree/c978824" rel="nofollow">https://github.com/pkg/errors/tree/c978824</a>