From 650916b302e2cf9da594cad21557ac2074694811 Mon Sep 17 00:00:00 2001 From: Daniel Burkard Date: Thu, 13 Apr 2023 16:32:51 +0200 Subject: [PATCH] Remove @escaping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiler can’t prove that the closures don’t escape the local scope. But since “the functions won't return until all of the closure calls have completed” this is not possible. We can use `withoutActuallyEscaping` to work around this. --- Sources/CollectionConcurrencyKit.swift | 126 ++++++++++++++----------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/Sources/CollectionConcurrencyKit.swift b/Sources/CollectionConcurrencyKit.swift index d39e46b..42f43ca 100644 --- a/Sources/CollectionConcurrencyKit.swift +++ b/Sources/CollectionConcurrencyKit.swift @@ -36,12 +36,14 @@ public extension Sequence { /// - parameter operation: The closure to run for each element. func concurrentForEach( withPriority priority: TaskPriority? = nil, - _ operation: @escaping (Element) async -> Void + _ operation: (Element) async -> Void ) async { - await withTaskGroup(of: Void.self) { group in - for element in self { - group.addTask(priority: priority) { - await operation(element) + await withoutActuallyEscaping(operation) { escapableOperation in + await withTaskGroup(of: Void.self) { group in + for element in self { + group.addTask(priority: priority) { + await escapableOperation(element) + } } } } @@ -62,17 +64,19 @@ public extension Sequence { /// - throws: Rethrows any error thrown by the passed closure. func concurrentForEach( withPriority priority: TaskPriority? = nil, - _ operation: @escaping (Element) async throws -> Void + _ operation: (Element) async throws -> Void ) async throws { - try await withThrowingTaskGroup(of: Void.self) { group in - for element in self { - group.addTask(priority: priority) { - try await operation(element) + try await withoutActuallyEscaping(operation) { escapableOperation in + try await withThrowingTaskGroup(of: Void.self) { group in + for element in self { + group.addTask(priority: priority) { + try await escapableOperation(element) + } } - } - // Propagate any errors thrown by the group's tasks: - for try await _ in group {} + // Propagate any errors thrown by the group's tasks: + for try await _ in group {} + } } } } @@ -119,16 +123,18 @@ public extension Sequence { /// the transformed values will match the original sequence. func concurrentMap( withPriority priority: TaskPriority? = nil, - _ transform: @escaping (Element) async -> T + _ transform: (Element) async -> T ) async -> [T] { - let tasks = map { element in - Task(priority: priority) { - await transform(element) + await withoutActuallyEscaping(transform) { escapableTransform in + let tasks = map { element in + Task(priority: priority) { + await escapableTransform(element) + } } - } - return await tasks.asyncMap { task in - await task.value + return await tasks.asyncMap { task in + await task.value + } } } @@ -150,16 +156,18 @@ public extension Sequence { /// - throws: Rethrows any error thrown by the passed closure. func concurrentMap( withPriority priority: TaskPriority? = nil, - _ transform: @escaping (Element) async throws -> T + _ transform: (Element) async throws -> T ) async throws -> [T] { - let tasks = map { element in - Task(priority: priority) { - try await transform(element) + try await withoutActuallyEscaping(transform) { escapableTransform in + let tasks = map { element in + Task(priority: priority) { + try await escapableTransform(element) + } } - } - return try await tasks.asyncMap { task in - try await task.value + return try await tasks.asyncMap { task in + try await task.value + } } } } @@ -214,16 +222,18 @@ public extension Sequence { /// except for the values that were transformed into `nil`. func concurrentCompactMap( withPriority priority: TaskPriority? = nil, - _ transform: @escaping (Element) async -> T? + _ transform: (Element) async -> T? ) async -> [T] { - let tasks = map { element in - Task(priority: priority) { - await transform(element) + await withoutActuallyEscaping(transform) { escapableTransform in + let tasks = map { element in + Task(priority: priority) { + await escapableTransform(element) + } } - } - return await tasks.asyncCompactMap { task in - await task.value + return await tasks.asyncCompactMap { task in + await task.value + } } } @@ -247,16 +257,18 @@ public extension Sequence { /// - throws: Rethrows any error thrown by the passed closure. func concurrentCompactMap( withPriority priority: TaskPriority? = nil, - _ transform: @escaping (Element) async throws -> T? + _ transform: (Element) async throws -> T? ) async throws -> [T] { - let tasks = map { element in - Task(priority: priority) { - try await transform(element) + try await withoutActuallyEscaping(transform) { escapableTransform in + let tasks = map { element in + Task(priority: priority) { + try await escapableTransform(element) + } } - } - return try await tasks.asyncCompactMap { task in - try await task.value + return try await tasks.asyncCompactMap { task in + try await task.value + } } } } @@ -309,16 +321,18 @@ public extension Sequence { /// within the returned array. func concurrentFlatMap( withPriority priority: TaskPriority? = nil, - _ transform: @escaping (Element) async -> T + _ transform: (Element) async -> T ) async -> [T.Element] { - let tasks = map { element in - Task(priority: priority) { - await transform(element) + await withoutActuallyEscaping(transform) { escapableTransform in + let tasks = map { element in + Task(priority: priority) { + await escapableTransform(element) + } } - } - return await tasks.asyncFlatMap { task in - await task.value + return await tasks.asyncFlatMap { task in + await task.value + } } } @@ -343,16 +357,18 @@ public extension Sequence { /// - throws: Rethrows any error thrown by the passed closure. func concurrentFlatMap( withPriority priority: TaskPriority? = nil, - _ transform: @escaping (Element) async throws -> T + _ transform: (Element) async throws -> T ) async throws -> [T.Element] { - let tasks = map { element in - Task(priority: priority) { - try await transform(element) + try await withoutActuallyEscaping(transform) { escapableTransform in + let tasks = map { element in + Task(priority: priority) { + try await escapableTransform(element) + } } - } - return try await tasks.asyncFlatMap { task in - try await task.value + return try await tasks.asyncFlatMap { task in + try await task.value + } } } }