simplified error handling

94 views
Skip to first unread message

Robert Engels

unread,
Jul 9, 2019, 1:25:36 PM7/9/19
to golan...@googlegroups.com

There is probably a similar proposal out-there, but if there is it hasn't surfaced, so I thought I would throw this out there.

It only requires 2 simple changes and is completely backwards compatible.

Currently, idiomatic Go error handling code looks like this:

v,err := somefunc()
if err!=nil {
// do something, in many cases, just return err
}

but a substantial amount of Go code has handling code like this:

v,err := somefunc()
if err!=nil {
goto some_error_handler
}

especially when there is common wrapping of errors, or retries.

With the proposal https://github.com/golang/go/issues/27165 accepted (at least Rob likes it), the error handling can be simplified by

v,:some_error_handler := somefunc()

which is essentially an if with goto macro.

The only other addition is a compiler provided 'function scoped' variable __error which is the last error returned by the last called function. This allows for trivial writing of the error handler block, as an example:

some_error_handler:
return errors.New("function failed",__error)

or even

some_error_handler:
retry++
if !__error.Permanent() && retry < N {
goto try_again
}
return errors.New("function failed",__error)

And since goto/labels in Go are already scoped, it allows inner handlers, as in:

for _,x := range blah {
v,:localerr := somefunc()
defer v.Close()
x,:localerr := anotherfunc(x)
continue
localerr:
return errors.New("unable to process item ...",__error)
}

I think this coupled with the xerrors enhancements would dramatically improve Go error handling. It is still very explicit, no hidden flow, plays nicely with defer, all returns are explicit, easy to read - no magic.

And if the function has minimal (or no) error handling required, it is written just as before.

A simple enhancement might be a pseudo handler label :return that always returns the 0 values for all results parameters, and __error as the err parameter. In many ways this would be similar to the current 'try' proposal.

The limitation of this solution is that nested calls are not possible as with 'try', but they would instead be done on multiple lines, as in:

v,:return = somefunc()
x,:return = another(v)
y,:return = yetanother(x)

which I think is easier to reason about.

Anyway, just a thought.






t hepudds

unread,
Jul 9, 2019, 2:59:19 PM7/9/19
to golang-nuts
Hi Robert,

You might be interested in the discussion here:


which include 3-4 comments discussing a similar (but not identical) idea from @josharian and @griesemer.

Best,
thepudds

Robert Engels

unread,
Jul 9, 2019, 3:51:26 PM7/9/19
to t hepudds, golang-nuts
The try/goto proposal is similar but I think slightly inferior for two reasons:

1) the try(try(try())) nesting is still possible, and I don't think that is easy reading
2) I don't see how the 'last error' to be used in the handler is defined, so how do you write the handler method ?

Furthermore, the __error can be used in conjunction with if err!=nil code, allowing for common error handling code in more complex if blocks.

I don't like that either proposal still makes chained function calls either impossible or hard to read, but a further thought, to make things more explicit would be to have function chaining automatically propagate/terminate on errors, so the following pattern could be used:

result,err := somefuncA().somefuncB().somefuncC()

where result (and its type) is always the return of somefuncC(), unless err is non-nil, and this is only valid for function that return a single value and an optional error.

I don't know how well something like this works with resource based returns, but the try() proposal suffers similarly.

Coupled with the labelled error handling, any complex error duplicative error handling can be easily isolated, making the logic easier to follow. I know the Go way states that the error handling is the logic, and in many cases that is true, but if your error handling is a return, or a wrap and a return, I don't think that holds true, and it obscures the function logic.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/d929c8ea-4048-4c08-aaf6-f2cf5316fe58%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



Liam

unread,
Jul 9, 2019, 10:54:40 PM7/9/19
to golang-nuts

Robert Engels

unread,
Jul 9, 2019, 11:04:43 PM7/9/19
to Liam, golang-nuts
That proposal is materially different, due to using handler functions, rather than the already existing scoped labels, both in terms of cognitive load - where is the function called - what does the stack look like?What about panics?, and in terms of lines of code (functions are far more verbose). The goto label is far simpler IMO. 

It also omits the __error variable which is the key to making the code simple and easy to reason about. 
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages