On Thu, Oct 27, 2022 at 06:23:31AM -0700, Gergely Brautigam wrote:

> I was wondering what's the consensus these days on handling defer errors?
> 
> Is it something like, overwriting the returned value, or using a list of 
> errors?
> 
> Either:
> 
> func test() (err error) {
>     defer func() {
>         if tmpErr := doSomething(); tmpErr != nil {
>             err = tmpErr
>         }    
>      }()
> 
>      err = doSomethingElse()
>      return err
> }
> 
> Or:
> func test() (errList []error) {
>     defer func() {
>         if tmpErr := doSomething(); tmpErr != nil {
>             errList = append(errList, tmpErr)
>         }    
>      }()
> 
>      if err := doSomethingElse(); err != nil {
>           errList = append(errList, err)
>      }
>      return errList
> }
> 
> Or even something else entirely? 

I think you're falling into a common trap: there's no need to have any sort of
consensus here because what you have demonstrated are two approaches to tackle
a problem, and they depend on what you're after.

One could easily add other variations.

For instance, often you want to Close() a resource on an error path out;
calling Close might itself return an error, and sometimes this error is less
severe than the "original" one. As an example, consider opening a network
socket and then doing some setup on it: say, socket creation went OK but the
setup failed, so you'd want to return the error about the failed setup
operation, and if closing the socket on the way out fails for some reason,
you could possibly ignore this error. Of may be you can log it.

On the other hand, if, say, you had a file opened for writing, wrote some data
to it, then some error has happened, and you're aborting the operation
returning that error, and closing of the file on the way out fails, this might
be a severe enough error because it might indicate some data which was written
got lost. What to do about it? There's no ready answer. You could construct a
custom error with a compound error message, like "error X has happened while
Y, and also closing of F has failed during cleanup".

You can also return a list of errors.
Or create a custom type implenting the error interface which holds a list of
errors.

Since Go 1.13 you can also make chains of errors; say, in your second example
you could do

  err = fmt.Errorf("doSomething failed with %s while handling %w",
    tmpErr, err)

and then have callers use errors.Is / errors.As or whatever other approach to
learn whether the error of their interest is in the error chain.

Note that in ether approach with returning multiple errors, the callers are to
be prepared to handle multiple errors; otherwise it's engineering for the sake
of engineering which leads to nothing more than code which is convoluted for
nothing.

So I'd recommend to start small, use the simplest approach possible until you
see clear need to use a more complex one, and then decide what looks/works
best in a particular case. I know, this is a glib piece of advice, but it's
really how the things are ;-)

-- 
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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/20221027175042.uhaakvkgnmbvqmh4%40carbon.

Reply via email to