typealias in a protocol: can only be used as generic constraint?


(Jonathan Bailey) #1

In the following code, I get an error on the line indicated (4th from the bottom) that "Protocol 'APIEndPoint' can only be used as a generic constraint because it has Self or associated type requirements” I understand this is because the APIEndPoint protocol contains a type alias. However I don’t understand why this means it can’t be used as a type? I simply want the init method I’m defining to be able to accept any object/enum that conforms to the APIEndPoint protocol.

protocol API {
    static var baseUrl : String { get };
}

protocol APIEndPoint {
    typealias ParentAPI : API
    var path: String { get }
    func urlString() -> String
}
extension APIEndPoint {
    func urlString() -> String {
        return ParentAPI.baseUrl + path;
    }
}

extension NSURL {
    convenience init(instance:APIEndPoint) { //error here
        self.init(string:instance.urlString())!
    }
}

If I change that line to `convenience init<T:APIEndPoint>(instance:T) {`then there is no problem.

Also if I split the APIEndPoint protocol in 2, like so:

protocol APIEndPointBase {
    var path: String { get }
    func urlString() -> String
}
protocol APIEndPoint : APIEndPointBase {
    typealias ParentAPI : API
}

Then the change the line with the error to:

convenience init(instance:APIEndPointBase) {

then there is also no error.

The ability to work around this error makes it seem like the error is pointless. Even if I used the ParentAPI typealias with the protocol definition, I don’t see why I can’t use it as I have in the first code segment. Why does having Self or associated type requirements mean that the protocol can only be used as a generic constraint?

Apologies if I have posted this incorrectly, this is the first time I’ve used a mailing list

Jonathan


(Jens Alfke) #2

I finally understand the reason, after reading [a draft of] objc.io's Advanced Swift book. You can’t use such a protocol as a type because if the aliased type appears in the API, it won’t be consistent between different objects that conform to the protocol but have different aliased types. For example, if a protocol Foo has a method foo() that returns a typealias T, then different objects conforming to Foo will return different types from their foo() methods, so there’s no type-safe way to represent that return value.

In your case you’ve got a typealias but you’re not using it in the protocol’s API. It looks like it’s just internally used inside the implementation of the urlString method. You can probably change your code around to get the same effect without using a typealias. Make baseURL a regular non-static method of API, then just have a property of APIEndpoint of type API.

—Jens

···

On Dec 28, 2015, at 3:43 PM, Jonathan Bailey via swift-users <swift-users@swift.org> wrote:

In the following code, I get an error on the line indicated (4th from the bottom) that "Protocol 'APIEndPoint' can only be used as a generic constraint because it has Self or associated type requirements” I understand this is because the APIEndPoint protocol contains a type alias. However I don’t understand why this means it can’t be used as a type?