RunPool bounded worker pool with indexed results

Go
package pool

import (
    "context"
    "sync"
)

// RunPool applies fn to indices [0,n) using a fixed number of goroutines.
// Results and errors are stored at their index, so input order is preserved.
func RunPool[T any](ctx context.Context, n, workers int, fn func(context.Context, int) (T, error)) ([]T, []error) {
    if workers < 1 {
        workers = 1
    }
    out, errs := make([]T, n), make([]error, n)
    idx := make(chan int, n)
    var wg sync.WaitGroup
    for w := 0; w < workers; w++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for i := range idx {
                out[i], errs[i] = fn(ctx, i)
            }
        }()
    }
    for i := 0; i < n; i++ {
        if err := ctx.Err(); err != nil {
            errs[i] = err
            continue
        }
        idx <- i
    }
    close(idx)
    wg.Wait()
    return out, errs
}