Returning error slice instead of single error

5,297 views
Skip to first unread message

Matt Sherman

unread,
Apr 30, 2014, 1:47:36 PM4/30/14
to golan...@googlegroups.com
A pattern I was thinking about: would it be better to conventionally return []error instead of a single error? Two reasons:

err != nil vs len(errs) > 0

First, comparison with nil is confusing, esp to newbies. It's fine here but less fine in other places. Might be nice to avoid it.

Second, funcs can legitimately return more than one error! Perhaps it's idiomatic to only return one, and perhaps it's a code smell to return more than one. Curious if these assumptions are conventional. I've had plenty of use cases where returning multiple errors felt like the right thing to do.

To be clear, my q is about patterns and conventions, not correctness. Would like to hear thoughts. Thanks.

Andy Balholm

unread,
Apr 30, 2014, 1:57:41 PM4/30/14
to golan...@googlegroups.com
Most functions will abort after their first error; so a single error is much more common than multiple errors. When you can have multiple errors, you can do something like http://play.golang.org/p/X2Q2aVJweW (a []error that fulfills the error interface itself).

DV

unread,
Apr 30, 2014, 2:02:09 PM4/30/14
to golan...@googlegroups.com
"error" is an interface. 
Any type can implement an interface. 
So make your own type that implements the "error" interface and voila. 

Also, "len" is only defined on strings, maps, arrays, slices, etc, so you can't do len(<interface variable>) before doing a type assertion first, anyway. 

Matt Jibson

unread,
Apr 30, 2014, 2:31:24 PM4/30/14
to Matt Sherman, golang-nuts
The app engine package has a MultiError type. It is used when fetching many entities from the datastore at once, where any number of them could be an error. It is a bit cumbersome to use because, if err != nil, you must then do a checked type assertion to a MultiError to see if it's one of those. But overall a fine method.



--
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.
For more options, visit https://groups.google.com/d/optout.

Matt Sherman

unread,
Apr 30, 2014, 2:53:04 PM4/30/14
to Andy Balholm, golan...@googlegroups.com
Andy, I like that...


On Wed, Apr 30, 2014 at 1:57 PM, Andy Balholm <andyb...@gmail.com> wrote:
Most functions will abort after their first error; so a single error is much more common than multiple errors. When you can have multiple errors, you can do something like http://play.golang.org/p/X2Q2aVJweW (a []error that fulfills the error interface itself).

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/zE-CrCoeziw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

Hariharan Srinath

unread,
May 1, 2014, 2:18:31 PM5/1/14
to golan...@googlegroups.com
I recently hit a situation where I considered returning []error since my function could hit multiple errors.

I decided not to go this route because if my function *could* go on after hitting an error then either-

a. From my function's pov it's not really an error but an event to ignore or log (and perhaps work around in parent / downstream code)

Or

b. The function is trying to do too many things and should be broken up into simpler units that return a single error to a parent which IS in a position to ignore or log errors where applicable

Keeping error as a single return value rather than a slice had a simplifying effect on my code.

Best regards
Srinath

Andy Balholm

unread,
May 1, 2014, 2:31:05 PM5/1/14
to golan...@googlegroups.com, Andy Balholm
For examples of functions that accumulate errors instead of aborting on the first one, see the go/scanner and go/parser packages.

Peter Bourgon

unread,
May 2, 2014, 9:13:08 AM5/2/14
to Andy Balholm, golang-nuts
Is the client's behavior going to be any different if it receives a
single error or multiple?

I sometimes do this:

func whatever() error {
var errors []string
for whatever {
if err := something(); err != nil {
errors = append(errors, err.Error())
}
}

if len(errors) > 0 {
return fmt.Errorf(strings.Join(errors, "; "))
}
return nil
}


On Thu, May 1, 2014 at 8:31 PM, Andy Balholm <andyb...@gmail.com> wrote:
> For examples of functions that accumulate errors instead of aborting on the
> first one, see the go/scanner and go/parser packages.
>
> --
> 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

Matt Sherman

unread,
May 2, 2014, 2:30:39 PM5/2/14
to golang-nuts
@Peter, on the client, it's not substantially different. It's a len() check instead of a nil check, which smells better to me, but not a big deal.

It might be slightly better for the client when debugging to see as many errors as are knowable. The first error in the func doesn't necessarily mean that the func needs to bail.

(Think about build errors, it's not just the first, one gets a list.)

Other side of the coin, accumulating errors where some are fatal and some aren't makes for complex code. Stateful. So perhaps it's a bad habit.



You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/zE-CrCoeziw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

s...@surul.com

unread,
May 3, 2014, 6:41:18 AM5/3/14
to golan...@googlegroups.com
@Matt

I've found that having a single error is usually sufficient except in the
following situation. There are N operations to execute where a single failed
operation could result in the batch being declared a failure. However, all
errors will need to be reported. A couple of examples:

  * Start a number of goroutines and collect their results.
 
  * Release a set of resources and track release errors without aborting.

My current solution is a small wrapper around a list of errors, so I can do the
following:


func myFunc() error {
errs := fault.NewErrorChain()
errs.Append(err1)
errs.Append(err2)
// etc
// or alternatively
errs := fault.Chain(err1, err2, err3)
// I then do
if errs.AsError() != nil {
return errs.AsError()
}
}

        or even

func myFunc2() (err error) {
errs := fault.NewErrorChain()
defer func() {
if errs.AsError() != nil {
err = errs.Append(err).AsError()
}
}()

res1 := Open()
defer func() { errs.Append(res1.Close()) }()
res2 := Open()
defer func() { errs.Append(res2.Close()) }()

}

The important thing is to not mix errors which should just be reported and
errors which indicate a failure.

Regarding the fact the comparison to nil is confusing to beginners I'd say it
is good to have that confusion since it'll hopefully result in them taking the
time to figure out what a nil comparison means. Given that nil is widely used
in Go, hiding that from a beginner will likely do more harm than good.

Sridhar
Reply all
Reply to author
Forward
0 new messages