8

Given:

protocol MyProtocol {
    typealias T
    var abc: T { get }
}

And a class that implements MyProtocol:

class XYZ: MyProtocol {
    typealias T = SomeObject
    var abc: T { /* Implementation */ }
}

How can I define an array of objects conforming to MyProtocol?

var list = [MyProtocol]()

Gives (together with a ton of SourceKit crashes) the following error:

Protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements

Even though the typealias is in fact defined in MyProtocol.

Is there a way to have a list of object conforming to a protocol AND having a generic constraint?

erudel
  • 482
  • 4
  • 11
  • possible duplicate of [Usage of protocols as array types and function parameters in swift](http://stackoverflow.com/questions/24888560/usage-of-protocols-as-array-types-and-function-parameters-in-swift) – Paulw11 Oct 24 '14 at 06:50
  • @Paulw11 My bad, I completely missed that one. – erudel Oct 24 '14 at 15:55

1 Answers1

3

The problem is about using the generics counterpart for protocols, type aliases. It sounds weird, but if you define a type alias, you cannot use the protocol as a type, which means you cannot declare a variable of that protocol type, a function parameter, etc. And you cannot use it as the generic object of an array.

As the error say, the only usage you can make of it is as a generic constraint (like in class Test<T:ProtocolWithAlias>).

To prove that, just remove the typealias from your protocol (note, this is just to prove, it's not a solution):

protocol MyProtocol {
    var abc: Int { get }
}

and modify the rest of your sample code accordingly:

class XYZ: MyProtocol {
    var abc: Int { return  32 }
}

var list = [MyProtocol]()

You'll notice that it works.

You are probably more interested in how to solve this problem. I can't think of any elegant solution, just the following 2:

  • remove the typealias from the protocol and replace T with AnyObject (ugly solution!!)
  • turn the protocol into a class (but that's not a solution that works in all cases)

but as you may argue, I don't like any of them. The only suggestion I can provide is to rethink of your design and figure out if you can use a different way (i.e. not using typealiased protocol) to achieve the same result.

Antonio
  • 71,651
  • 11
  • 148
  • 165
  • This actually makes sense if you think about it. Since the return type of abc isn't known at compile time, you can't reliably use the results in a type safe fashion. An array of MyProtocol's makes no sense because they don't all have the same functionality. – David Berry Oct 24 '14 at 06:41
  • 1
    @David: I do not agree with that. Struct and classes with generics have the same problem. Other languages (C#, java, just to name a few) allow protocols with generics. And however an array of `MyProtocol` **do have** the same functionality - what is different is that each element can have the functionality implemented in a different way - but the same concept applies to using a base class as array element type and put instances of objects inherited from it. – Antonio Oct 24 '14 at 06:51
  • @Antonio Thanks, as I see it casting is probably the only solution here. In my previous attempt I was indeed using a class one generic parameter, but Swift does not allow (yet) to have a non-generic type that inherits from a generic one – `XYZ: ABC` is not allowed. – erudel Oct 24 '14 at 15:51
  • @erudel Yeah that's another thing of swift that I miss... although not as important as having **usable** generic protocols. – Antonio Oct 24 '14 at 15:54