Swift bounded worker pool with Result collection

Swift
import Foundation

func runPool<T, R>(_ items: [T], workers: Int, task: @escaping (T) throws -> R) -> [Result<R, Error>] {
    let queue = DispatchQueue(label: "pool", attributes: .concurrent)
    let gate = DispatchSemaphore(value: max(1, workers))
    let lock = NSLock()
    let group = DispatchGroup()
    var results = [Result<R, Error>?](repeating: nil, count: items.count)
    for (index, item) in items.enumerated() {
        gate.wait()
        group.enter()
        queue.async {
            lock.lock()
            results[index] = Result { try task(item) }
            lock.unlock()
            gate.signal()
            group.leave()
        }
    }
    group.wait()
    return results.compactMap { $0 }
}