Lorenzo B
Lorenzo B

Reputation: 33428

Cannot explicitly specialize a generic function when using closures

I have a function like this:

func register<T: Routable>(_ action: Action, withCallback callback: @escaping (T?) -> Void) {
    notificationCenter.addObserver(forName: actionMap[action], object: nil, queue: .main, using: { notification in
    let routable = T(userInfo: notification.userInfo)
        callback(routable)
    })
}

Where Routable is defined like:

protocol Routable {
    init?(userInfo: [AnyHashable: Any]?)
}

When I try to use it, I receive

Cannot explicitly specialize a generic function

This is the usage:

controller.register<Navigate>(Action.navigate) { [unowned self] navigate in
    // do something
}

Any idea to make the compiler happy?

Upvotes: 3

Views: 1141

Answers (2)

Rob Napier
Rob Napier

Reputation: 299325

I believe this is purely a syntax issue. You can't pass the type parameter directly like this. You need to "fill in the type hole" instead. To do that, you need to add the type to navigate:

controller.register(Action.navigate) { [unowned self] (navigate: Navigate?) in ... }

Sometimes that syntax is annoying because it buries the type. You can improve it by rewriting the signature of register this way:

func register<T: Routable>(action: Action, returning: T.type,
                           withCallback callback: @escaping (T?) -> Void)

You'd then call it this way:

controller.register(action: .navigate, returning: Navigate.self) { 
    [unowned self] navigate in 
    // ...
}

The returning parameter isn't directly used in the function. It just provides a more explicit way to specialize the function.

Upvotes: 5

Scott Thompson
Scott Thompson

Reputation: 23701

It's really hard to say without seeing a more complete code example... but basically what the compiler is telling you is "You told me you want the register function to be generic (because it has a type parameter) but then you are also trying to tell me exactly what type to use and I don't like that".

The point at which you are "explicitly specializ[ing]" the function is when you add a specific type parameter in the call:

controller.register<Navigate>...
# Right Here       ^^^^^^^^^^

The compiler wants the flexibility to determine what type of register function to call. I suspect what you want is:

controller.register(Action.navigate) {...

Where you do not explicitly specialize the generic function but let the complier figure out which specialization to use.

Upvotes: 0

Related Questions