Trying to solve a problem that's irrelevant for the moment, I tried to do something in Swift that's pretty normal in C# code: define a couple of protocols to describe a producer-consumer relationship, and then to provide a concrete implementation of the producer:
protocol Consumer
{
func someInterestingThing( producer: Producer )
func anotherInterestingThing( producer: Producer, happenedBefore: Bool )
}
protocol Producer
{
func addConsumer( consumer: Consumer )
func removeConsumer( consumer: Consumer )
}
class ConcreteProducer : Producer
{
private var _consumers = Set<Consumer>()
func addConsumer( consumer: Consumer ) {
_consumers.insert(consumer);
}
func removeConsumer( consumer: Consumer ) {
_consumers.remove(consumer);
}
}
This doesn't work, because my Consumer
protocol isn't Hashable
, which it must be to go into a Swift Set
. (In C#, this doesn't come up, because all objects inherit hashable-nature.)
OK, so I'll define my protocol as inheriting from Hashable; not exactly elegant, but should work. Nope! Now all of the places I use it, I get:
error: protocol 'Consumer' can only be used as a generic constraint
because it has Self or associated type requirements
Apparently, this is because Hashable
inherits Equatable
, which defines ==
on itself. Literally in terms of Self
, which places some extra restrictions on how the type can be used.
Maybe there's some generics magic that I don't grok yet that would allow me to declare the collection and the methods with types the compiler would be happy with?
I might try to do something smelly like define the storage in more abstract terms, and enforce protocol conformance on the add and remove methods. But AnyObject
doesn't implement Hashable
. (Is there some better alternative?)
I could try to box the consumers in a private wrapper that implements Hashable
, but I'd need to be able to compare those wrappers, which would depend on the identity of the consumer, and then it seems like I'm back in the same boat.
All I want is to keep a bunch of these things in a collection, without knowing any more about them than absolutely necessary. How do I do that?