A common mistake is rebuilding goroutine coordination with sync.WaitGroup plus shared error slices, mutexes, or channels. errgroup.Group.Go starts each error-returning function in a managed goroutine, while g.Wait reports an error from the group. section 9.13
Parallel calls with one returned error
For multiple calls over different subsets of circles, create the group with errgroup.WithContext(ctx), then call g.Go for each circle. Capture the loop index and value, return foo's error, and write successful results to that index. After launching all calls, use if err := g.Wait(); err != nil { return nil, err }; otherwise return results. This preserves the simple indexed result layout without separately collecting errors. section 9.13
func handler(ctx context.Context, circles []Circle) ([]Result, error) {
results := make([]Result, len(circles))
g, ctx := errgroup.WithContext(ctx) #1
for i, circle := range circles {
i := i
circle := circle
g.Go(func() error { #2
result, err := foo(ctx, circle)
if err != nil {
return err
}
results[i] = result
return nil
})
}
if err := g.Wait(); err != nil { #3
return nil, err
}
return results, nil
}
Inspect the complete handler pattern, including loop-variable capture, error propagation, indexed results, and group waiting. section 9.13
