I have a protocol with an associated type:
protocol MyProtocol {
associatedtype Q
}
Now I'd like to have an enum like
enum MyEnum<Q> {
case zero
case one(MyProtocol)
case two(MyProtocol, MyProtocol)
}
where each associated value has Q
as its associated type. This doesn't work:
enum MyEnum<Q> {
case zero
case one<P: MyProtocol where P.Q == Q>(P)
case two<P1: MyProtocol, P2: MyProtocol where P1.Q == Q, P2.Q == Q>(P1, P2)
}
Apparently, individual enum members can't have their own generic constraints.
The only thing I can think of is to move those constraints to the enum declaration, but this fixates the associated types. To demonstrate why that's not what I want, this is what I'd like to be able to do:
struct StructA: MyProtocol {
typealias Q = Int
}
struct StructB: MyProtocol {
typealias Q = Int
}
var enumValue = MyEnum.one(StructA())
enumValue = .two(StructB(), StructA())
enumValue = .two(StructA(), StructB())
Is there a way around this limitation?
Type erasure. The answer is always type erasure.
What you need is an AnyProtocol type:
struct AnyProtocol<Element>: MyProtocol {
typealias Q = Element
// and the rest of the type-erasure forwarding, based on actual protocol
}
Now you can create an enum that uses them
enum MyEnum<Q> {
case zero
case one(AnyProtocol<Q>)
case two(AnyProtocol<Q>, AnyProtocol<Q>)
}
For a deeper discussion of how to build the type erasers see A Little Respect for AnySequence.
Swift cannot discuss PATs (protocols with associated types) as real types or even abstract types. They can only be constraints. In order to use it as even an abstract type, you have to distill it into a type eraser. Luckily this is quite mechanical and in most cases not difficult. It's so mechanical that eventually the compiler will hopefully do the work for you. But someone has to build the box, and today that's you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With