Skip to content

error nil check weirdness with Group.Wait #57

@ccampo133

Description

@ccampo133

It's not uncommon to see code like this:

package main

import (
	"github.com/hashicorp/go-multierror"
)

type server struct{}

func (s *server) Run() error {
	g := new(multierror.Group)
	g.Go(
		func() error {
			// ...code to do some work here...
			return nil
		},
	)
	g.Go(
		func() error {
			// ...code to do some MORE work here...
			return nil
		},
	)
	return g.Wait()
}

func main() {
	s := server{}
	if err := s.Run(); err != nil {
		panic("error while running")
	}
}

However, there is a subtle bug here. The main function will always panic, despite no error being returned by any of the Goroutines managed by the group g. This is due to the fact that Go treats interfaces as "fat pointers". See:

A "fix" is to add the following nil check to the Run method above:

func (s *server) Run() error {
	g := new(multierror.Group)
	g.Go(
		func() error {
			// ...code to do some work here...
			return nil
		},
	)
	g.Go(
		func() error {
			// ...code to do some MORE work here...
			return nil
		},
	)
	if err := g.Wait(); err != nil {
		return err
	}
	return nil

	//... or alternatively: return g.Wait().ErrorOrNil()
}

This is certainly not intuitive, and relies on type-inference to solve the problem. I've added a test to my fork of this repo to clearly demonstrate the issue:

My question - is there a good reason why Group.Wait returns *Error instead of just error, which is more idiomatic? Returning error would eliminate this weirdness seen here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions