Aaron Hayman Aaron Hayman - 6 months ago 73
Swift Question

Swift 3: Nested Generic Type

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)

On the assignment (
let pendingRequest: Request = ...
), I get this error:

Cannot convert value of type 'Set<Key>' to specified type 'Set<_>'

I cannot figure out how to fix this. It seems like the compiler can't recognize the type information for

Note: Obviously, the class is much larger that this. I copied and pasted out the relevant code instead of inserting 300 lines.


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)

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.