multierr

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 23, 2020 License: MIT Imports: 3 Imported by: 5

README

multierr

A simple multiple error holder for Go

Motivation

Sometimes you need to return more than one error from a function. The multierr package provides a simple slice of error instances called Error, which meets the error interface.

The way to use this package is via the package-level function Append.

Usage

package main

import (
	"errors"
	"fmt"

	"github.com/jonbodner/multierr"
)

func CallThing(i int) error {
	if i%2 == 1 {
		return errors.New("Error")
	}
	return nil
}

func DoSomethingThatMakesErrors(numCalls int) error {
	var out error
	for i := 0; i < numCalls; i++ {
		e := CallThing(i)
		out = multierr.Append(out, e)
	}
	return out
}

func main() {
	for i := 1; i < 5; i++ {
		fmt.Println("For i == ", i)
		e := DoSomethingThatMakesErrors(i)
		if e != nil {
			switch e := e.(type) {
			case multierr.Error:
				// you can use slice-supporting built-in functions,
				// like len or range
				fmt.Println(len(e), "errors found")
			default:
				fmt.Println("This is a normal error")
			}
			fmt.Println(e.Error())
		} else {
			fmt.Println("No errors!")
		}
	}
}

The Append function will do the right thing if e is nil (it will return its first parameter), so you don't need to wrap calls to it in if e != nil if you are just aggregating errors.

It is very important to notice that out in DoSomethingThatMakesErrors is declared to be of type error, not multierr.Error. This is due to the way that Go handles interface types and nil. For a variable of a non-interface type, having a nil value makes the variable equal to nil. For a variable of an interface type, both the type must be undefined and the value must be nil in order to make the variable equal to nil.

Look at this example:

package main

import (
	"fmt"

	"github.com/jonbodner/multierr"
)

func meErr() error {
	var out multierr.Error
	fmt.Println(out == nil)
	return out
}

func main() {
	e := meErr()
	if e != nil {
		fmt.Println("There was an error")
	}
}

If you run this code, it will print out:

true
There was an error

If the type of out is multierr.Error and no errors are ever appended to it, out is equal to nil in the meErr function. But once the value is returned, it is assigned to a variable of type error, which is an interface. The returned value has a type of multierr.Error and a value of nil. This is sufficient for it to be considered non-nil, which makes e != nil true.

The multierr.Append function uses reflection to properly handle parameters where the underlying value is nil. This will work correctly:

package main

import (
	"github.com/jonbodner/multierr"
	"fmt"
	"errors"
)

func main() {
	var e error
	var e2 multierr.Error

	e = multierr.Append(e, e2)
	checkError(e)

	e = multierr.Append(e, errors.New("This is an error"))
	checkError(e)

	e = multierr.Append(e, errors.New("I'm a second error"))
	checkError(e)

	e2 = multierr.Error{errors.New("I'm a third error"), errors.New("I'm a fourth error")}
	e = multierr.Append(e, e2)
	checkError(e)
}

func checkError(e error) {
	fmt.Println("is nil",e == nil)
	switch e := e.(type) {
	case multierr.Error:
		fmt.Println("This is a multierr of length",len(e))
	default:
		fmt.Println("This is a single error")
	}
}

If you run this code, it will print out:

is nil true
This is a single error
is nil false
This is a single error
is nil false
This is a multierr of length 2
is nil false
This is a multierr of length 4
Is and As

multierr.Error supports the errors.Is and errors.As functions that were added in Go 1.13. The functions return true if any of the errors contained in the multierror match the target for Is or As. errors.Is will also return true when you compare two multierrors with identical contents.

Wrapping vs. Multierror

When would you want to wrap an error vs. using a multierror? A multierror is useful when there are multiple simultaneous problems (such as multiple validation failures for a single struct). Wrapping errors is useful when there is a single error, but you want to add additional information to describe the error or the place in the code where it happened.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Append

func Append(e1 error, e2 error) error

Append takes in two errors and returns one error that combines the two. If either parameter is nil, the other parameter is returned. If the first parameter is an Error, then the function returns the second parameter appended to the first. Otherwise, the function returns an Error containing both parameters.

Types

type Error

type Error []error

func (Error) As

func (me Error) As(err interface{}) bool

As checks to see if any of the errors contained within Error match the supplied type.

func (Error) Error

func (me Error) Error() string

Error prints out all the Errors contained within the Error. Error prints each contained error on its own line (a \n is appended to the output)

func (Error) Is

func (me Error) Is(err error) bool

Is performs two different checks. First it checks if the supplied error is a multierror.Error. If so, Is returns true if all the errors match. If the supplied error is not a multierr.Error, Is checks to see if any of the errors contained within the multierr.Error matches the supplied error.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL