hpique
hpique

Reputation: 120324

Generic closure in protocol

I'd like to use a generic closure in a protocol. For example:

protocol Fetcher {

    func fetchWithSuccess<T>(success doSuccess : (T) -> (), failure doFailure : ((NSError?) -> ()))

}

I thought implementing such a protocol would work something like this:

class SimpleFetcher<T> : Fetcher {

    let thing : T

    init(thing : T) {
        self.thing = thing
    }

    func fetchWithSuccess<T>(success doSuccess : (T) -> (), failure doFailure : ((NSError?) -> ())) {
        doSuccess(self.thing) // Fails
    }
}

However, the above code fails with error 'T' is not convertible with 'T' in doSuccess(self.thing). What am I missing?

Might be worth mentioning that when using a class there is no problem:

class Fetcher<T> {

    func fetchWithSuccess(success doSuccess : (T) -> (), failure doFailure : ((NSError?) -> ())) {}

}

class SimpleFetcher<T> : Fetcher<T> {

    let thing : T

    init(thing : T) {
        self.thing = thing
    }

    override func fetchWithSuccess(success doSuccess : (T) -> (), failure doFailure : ((NSError?) -> ())) {
        doSuccess(self.thing)
    }
}

Upvotes: 1

Views: 978

Answers (2)

Antonio
Antonio

Reputation: 72750

There are 2 things to fix:

Do not make the method generic, because the class is already using generics

The class already defines the T generic type - if you use <T> in the method, the T type has no relationship with the type defined at class level - that explains why you have such a strange error message T is not convertible to T. To verify that, just change the generic name in the function from T to something else.

So your function should look like:

func fetchWithSuccess(success doSuccess : (T) -> (), failure doFailure : ((NSError?) -> ())) {
    doSuccess(self.thing) // Fails
}

Define a type alias in the protocol

You also have to define a type alias in the protocol, which is conceptually similar to generics for classes and structs (read Type Alias Declaration and Protocol Associated Type Declaration)

protocol Fetcher {
    typealias V
    func fetchWithSuccess(success doSuccess : (V) -> (), failure doFailure : ((NSError?) -> ()))
}

Upvotes: 2

Nate Cook
Nate Cook

Reputation: 93276

There are two changes you need to make -- one in your protocol and one in your class.

In the protocol, define a typealias for the associated type, and have your method use that alias as the type. The methods in a generic class don't need to be generic themselves -- the methods use the generic type of the class itself.

protocol Fetcher {
    typealias Element

    func fetchWithSuccess(success doSuccess : (Element) -> (), failure doFailure : ((NSError?) -> ()))
}

Then in your class, declare that typealias to be T, and remove the generic syntax from the method:

class SimpleFetcher<T> : Fetcher {
    typealias Element = T

    let thing : T

    init(thing : T) {
        self.thing = thing
    }

    func fetchWithSuccess(success doSuccess : (T) -> (), failure doFailure : ((NSError?) -> ())) {
        doSuccess(self.thing) // Fails
    }
}

Testing:

let s = SimpleFetcher(thing: "Hello")
s.fetchWithSuccess(success: { str in println(str) }, failure: { error in println(error) })
// "Hello"

Upvotes: 3

Related Questions