Swift Tip: Without Actually Escaping
In Swift Talk 90, we wrote a concurrent implementation of map
. The interface looks like this:
extension Array {
func concurrentMap<B>(_ transform: @escaping (Element) -> B) -> [B] {
// ...
}
}
The transform
function is marked as @escaping
(the compiler automatically infers this for us). However, even though the transform
function is used in a queue, it's not actually escaping: the function isn't used or referenced after the concurrentPerform
returns. Ashton Williams sent us a tweet:
In addition to handling throws like you mentioned, this parallelMap function could also use withoutActuallyEscaping(_:do:) to remove escaping from its own signature
His suggestion definitely cleans up the interface, and is generally a good idea in cases where the compiler thinks the closure is escaping, but the closure isn't actually escaping.
However, after looking at our code some more, we found that the @escaping
parameter was an artifact of a previous version (before refactoring). It turns out that we can remove @escaping
without having to use withoutActuallyEscaping(_:do:)
:
extension Array {
func concurrentMap<B>(_ transform: (Element) -> B) -> [B] {
let result = ThreadSafe(Array<B?>(repeating: nil, count: count))
DispatchQueue.concurrentPerform(iterations: count) { idx in
let element = self[idx]
let transformed = transform(element)
result.atomically {
$0[idx] = transformed
}
}
return result.value.map { $0! }
}
}
This code has a simple API and a simple implementation. Our thanks to Ashton for leading us towards the right path. ๐