I've got some weird behavior from the compiler I can't figure out (Xcode 8, Swift 3) as I'm trying to migrate my code from Swift 2. I think it has to do with tuples, but I'm not entirely certain.
I've got a generic class that defines a couple generics. In those generics, I've also got a couple type alias's setup and a function that uses them:
open class GuardPool <Key: Hashable, Resource> {
public typealias ResourceCallback = ([Resource]) -> Void
public typealias Request = (keys: Set<Key>, cb: ResourceCallback)
fileprivate var pendingRequests: [Request] = []
open func request(_ keys: Set<Key>, cb: ([Resource]) -> Void) {
let pendingRequest: Request = (keys: keys, cb: cb)
pendingRequests.append(pendingRequest)
}
}
let pendingRequest: Request = ...
Cannot convert value of type 'Set<Key>' to specified type 'Set<_>'
Set
The error message is misleading. If you remove the (unnecessary) explicit type annotation in the second line
let pendingRequest = (keys: keys, cb: cb)
then the compiler states that
error: non-escaping parameter 'cb' may only be called note: parameter 'cb' is implicitly non-escaping
which reveals the actual problem: The cb
parameter must be marked as @escaping
because
it is stored in a property and might be called at a later time,
after returning from the request
method:
open func request(_ keys: Set<Key>, cb: @escaping ([Resource]) -> Void) {
let pendingRequest = (keys: keys, cb: cb)
pendingRequests.append(pendingRequest)
}
In Swift 2, closures were escaping by default and could be marked
with @noescape
. In Swift 3, closures are non-escaping by default,
compare SE-0103: Make non-escaping closures the default.