I have a protocol called playable which requires implementation of func play()
. Class Damage
and DrawACard
both conform to the protocol
protocol Playable: class {
func play(game: Game, value: Int) -> Player
}
class Damage: Playable, Hashable, Equatable {
var hashValue: Int = 1
static func ==(lhs: Damage, rhs: Damage) -> Bool {
return lhs.hashValue == rhs.hashValue
}
func play(game: Game, value: Int) -> Player {
// do stuff
return game.activePlayer
}
}
class DrawACard: Playable, Equatable, Hashable {
var hashValue: Int = 2
static func ==(lhs: DrawACard, rhs: DrawACard) -> Bool {
return lhs.hashValue == rhs.hashValue
}
func play(game: Game, value: Int) -> Player {
// do stuff
return game.activePlayer
}
}
My issue is the following:
class Card {
var id: Int
var name: String
var cost: Int
var description: String
var target: Target
var abilities: [Playable: Int]
var cardType: CardType
init(id: Int, name: String, cost: Int, description: String, cardType: CardType, target: Target, abilities: [Playable: Int]) {
self.id = id
self.name = name
self.cost = cost
self.description = description
self.cardType = cardType
self.abilities = abilities
self.target = target
}
}
The 6th variable in the class abilities
throws an error when I try to put a protocol as the key. It gives me: Type 'Playable' does not conform to protocol 'Hashable'
.
I want the abilities variable to have a key of an object that implements Playable
and a value of Int
, eg. abilities = [DrawACard: 5]
How can I go about doing that?
I would change the definition of the classes to this, so the classes inherit from a Hashable base class:
This kind of mixing protocols with class inheritance is notoriously complicated. The answer to your specific issue is you can't directly, because if you make
Playable
conform toHashable
, you can't make an array of them.The simplest solution is don't use a protocol here. Just make an abstract class. Swift isn't very good at abstract classes, but they will make most of these problems go away.
Given your specific case, I might also consider using an enum for
Playable
rather than a protocol. That would likely make things simpler.From there, the solutions get a bit more complicated. For example, you could create a
ClassDictionary
along the lines of my ClassSet experiment. Or you can build a type-eraser. But it gets ugly.You could also consider switching from a class to a struct, and getting rid of the Dictionary, and just using an Array. If the point of the
Int
is a count, then just have multiple copies of the struct. If the point of theInt
is a parameter (like "how much damage"), then that should be inside theDamage
struct, not in a dictionary.(Unrelated note, your hash values are very strange. They'll work, but this isn't how hashes are meant to function.)
As an example of where I'm going with this, I'm seeing something like:
This switched everything to immutable structs on purpose; I believe everything you're describing here are really value types.