Skip to content

lestrrat-go/merror

Repository files navigation

merrors

Simple multi-error error type for Go.

tl;dr:

  • Sometimes you want multiple errors to be bundled into a single error.
  • Whereas other libraries directly act on the error object, this package uses a builder to create errors. This significanly reduces the complexity of the error object itself.
  • This unique design allows for graceful handling of collecting errors from multiple goroutines, and also to properly return a nil error when there are no errors.

DESCRIPTION

Simple usage:

package merror_test

import (
  "fmt"

  "github.com/lestrra-go/merror"
)

func ExampleMerror() {
  err1 := fmt.Errorf(`first error`)
  err2 := fmt.Errorf(`second error`)
  err3 := fmt.Errorf(`third error`)

  err := merror.NewBuilder().
    Error(err1).
    Error(err2).
    Error(err3).
    Build()

  for _, suberr := range err.Errors() {
    fmt.Printf("%s\n", suberr.Error())
  }

  // OUTPUT:
  // first error
  // second error
  // third error
}

source: merror_example_test.go

Use with multiple goroutines:

package merror_test

import (
  "context"
  "fmt"
  "sort"
  "sync"

  "github.com/lestrra-go/merror"
)

func ExampleMerror_MultipleGoroutines() {
  b := merror.NewBuilder()
  ctx := b.NewContext(context.Background())

  var wg sync.WaitGroup
  wg.Add(10)

  for i := 0; i < 10; i++ {
    i := i
    go func(ctx context.Context) (err error) {
      defer wg.Done()
      defer merror.AddToContext(ctx, &err)

      return fmt.Errorf(`%d`, i)
    }(ctx)
  }

  wg.Wait()

  // note: in order to make the the output deterministic,
  // we're having to sort the errors
  errs := b.Build().Errors()
  sort.Slice(errs, func(i, j int) bool {
    return errs[i].Error() < errs[j].Error()
  })
  for _, err := range errs {
    fmt.Printf("%s\n", err)
  }

  // OUTPUT:
  // 0
  // 1
  // 2
  // 3
  // 4
  // 5
  // 6
  // 7
  // 8
  // 9
}

source: merror_goroutine_example_test.go

Also, this package works great when you actually want to detect if there were any errors at all. When the Builder is not passed any errors before Build() is called, then the Build() method returns nil, so you can actuall check if there were any errors in an idiomatic way.

package merror_test

import (
  "context"
  "fmt"
  "sync"

  "github.com/lestrra-go/merror"
)

func ExampleMerror_NoErrors() {
  b := merror.NewBuilder()
  ctx := b.NewContext(context.Background())

  var wg sync.WaitGroup
  wg.Add(10)

  for i := 0; i < 10; i++ {
    go func(ctx context.Context) (err error) {
      defer merror.AddToContext(ctx, &err)
      defer wg.Done()

      // No errors!
      return
    }(ctx)
  }

  wg.Wait()

  if errs := b.Build(); errs != nil {
    fmt.Printf("%s\n", errs)
  }

  // OUTPUT:
}

source: merror_noerror_example_test.go

About

Error representing multiple errors

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published