Have spent a while in the py/js world but have switched to rust/go for part of this year -- biggest change is boilerplate relating to error handling. Automatic stack capture for exceptions is something my language could conceivably do on my behalf. Writing even 3 lines of code per function to propogate up the error is a huge pain, especially because it pollutes the return type -- MustWhatever() in go is much easier t…
You're fighting the language. Unless you're writing prototypes where crashing doesn't matter, you should be writing error handling code first, and your business logic second -- so-called "sad path first" programming, c.f. "happy-path first" programming that you usually do in languages with exceptions like Python.
Eris – A better way to handle, trace, and log errors in Go
11–20 of 61 posts
Re: Eris – A better way to handle, trace, and log errors in Go
#12Have spent a while in the py/js world but have switched to rust/go for part of this year -- biggest change is boilerplate relating to error handling. Automatic stack capture for exceptions is something my language could conceivably do on my behalf. Writing even 3 lines of code per function to propogate up the error is a huge pain, especially because it pollutes the return type -- MustWhatever() in go is much easier t…
Re: Eris – A better way to handle, trace, and log errors in Go
#13Have spent a while in the py/js world but have switched to rust/go for part of this year -- biggest change is boilerplate relating to error handling. Automatic stack capture for exceptions is something my language could conceivably do on my behalf. Writing even 3 lines of code per function to propogate up the error is a huge pain, especially because it pollutes the return type -- MustWhatever() in go is much easier t…
Rust has a postfix `?` operator to propagate errors up the call stack.
The ? operator is better than nothing, but I still need my error types to match all the way up the stack
Re: Eris – A better way to handle, trace, and log errors in Go
#14Have spent a while in the py/js world but have switched to rust/go for part of this year -- biggest change is boilerplate relating to error handling. Automatic stack capture for exceptions is something my language could conceivably do on my behalf. Writing even 3 lines of code per function to propogate up the error is a huge pain, especially because it pollutes the return type -- MustWhatever() in go is much easier t…
Re: Eris – A better way to handle, trace, and log errors in Go
#15Earlier quoted context omitted.
Rust has a postfix `?` operator to propagate errors up the call stack.
as I understand it, this requires an Option or Result return type that matches the type of the statement The ? operator is better than nothing, but I still need my error types to match all the way up the stack
Re: Eris – A better way to handle, trace, and log errors in Go
#16Earlier quoted context omitted.
You're fighting the language. Unless you're writing prototypes where crashing doesn't matter, you should be writing error handling code first, and your business logic second -- so-called "sad path first" programming, c.f. "happy-path first" programming that you usually do in languages with exceptions like Python.
I'm super into the error handling philosophies of Go and Rust, but I don't think either of them is "sad path first". That implies that you can think about your error cases in a meaningful way before you have concrete business logic, which seems unlikely.
func (t *Thing) Process(id int) (string, error) {
return "", fmt.Errorf("not implemented")
}
and then filling them in gradually like func (t *Thing) Process(id int) (string, error) {
dat, err := t.store.Read(id)
if err != nil {
return "", fmt.Errorf("error reading ID: %w", err)
}
cert, err := dat.ExtractCertificate()
if err != nil {
return "", fmt.Errorf("error extracting certificate: %w", err)
}
return cert.Name(), nil
}
and explicitly not func (t *Thing) Process(id int) (string, error) {
dat, _ := t.store.Read(id) // TODO: error handling
cert, _ := dat.ExtractCertificate() // TODO: error handling
return cert.Name(), nil
}
Writing code this way, explicit error handling upfront, is fundamental to reliability (for a large class of applications).Re: Eris – A better way to handle, trace, and log errors in Go
#17Re: Eris – A better way to handle, trace, and log errors in Go
#18Earlier quoted context omitted.
Rust has a postfix `?` operator to propagate errors up the call stack.
as I understand it, this requires an Option or Result return type that matches the type of the statement The ? operator is better than nothing, but I still need my error types to match all the way up the stack
And if you think about it, defining those operator is a good practice as it tells your program how to handle errors.
Re: Eris – A better way to handle, trace, and log errors in Go
#19Have spent a while in the py/js world but have switched to rust/go for part of this year -- biggest change is boilerplate relating to error handling. Automatic stack capture for exceptions is something my language could conceivably do on my behalf. Writing even 3 lines of code per function to propogate up the error is a huge pain, especially because it pollutes the return type -- MustWhatever() in go is much easier t…
I don’t buy the “huge pain” argument. I write lots of Python and Go, and the error boilerplate is a non-issue. I also appreciate that it’s explicit instead of implicit.
Forgetting that a function returns an error:
...
foo() // foo returns an error that isn't being handled.
...
Forgetting to check the error returned by a function. Note a linter won't pick this up since the err variable is used later. ...
err := foo()
err = bar() // The previous error will go unhandled.
...
Accidentally typing return nil instead of return err: ...
if err != nil {
return nil
}
...
And in the case of the errors library, there's times where I will call a builtin function that returns an error and forget to call errors.WithStack. Every once in a while I'll come across an error without a stack trace and I'll have to hunt down where it came from: ...
err := json.Unmarshal(bytes, &obj)
if err != nil {
return err // should be errors.WithStack(err)
}
...
All of these issues look just like normal bug free Go code. On the basis that I've introduced more bugs this way, I prefer Python style error handling by far.Re: Eris – A better way to handle, trace, and log errors in Go
#20Earlier quoted context omitted.
I don’t buy the “huge pain” argument. I write lots of Python and Go, and the error boilerplate is a non-issue. I also appreciate that it’s explicit instead of implicit.
It's not just a pain to write. I've accidentally introduced way more bugs through Go style error handling than through Python style error handling. Some examples: Forgetting that a function returns an error: ... foo() // foo returns an error that isn't being handled. ... Forgetting to check the error returned by a function. Note a linter won't pick this up since the err variable is used later. ... err := foo() err =…
Not to me.