Reputation: 12178
I’m making a Farm where everything that can be grown conforms to Growable protocol. When you plant a plant, you call this func:
myFarm.planting<T: Growable>(qty: Int, of: T.Type) -> Farm
Now I want each instance of Farm to have a dictionary instance var like:
var crops = [Growable.Type: Int]
The problem is, even if I make the Growable protocol inherit Hashable, this does not help the Growable type become Hashable.
In other words, even if I add an extension to Growable like this:
extension Growable {
static func hashValue {
// return some hash
}
}
... still the Growable type is not Hashable, since the Hashable protocol only concerns instances of types but not the types themselves.
Well, normally I would give up and say, “I am stupid, do not attempt this further.”
However this is Swift, so I figure there must be a way to bend the language to my will, whether by making a new StaticHashable protocol and then extending the Dictionary type with a new subscript method accepting this, or by modding Swift’s source code itself and then making a pitch to the Evolution list.
But before I go down either of those paths, I thought it wise to ask you geniuses if there is already a way to do what I want, or whether doing this is incredibly stupid and you will present me with the obviously superior approach that I was unbelievably daft to have somehow missed all along.
Note: my opinion is that Types themselves should be able to statically adhere to protocols whose funcs are not declared as static, since why should the sender of a message care whether the entity that responds is an immortal God or an ephemeral Creature, made in some God’s image?
Upvotes: 3
Views: 2040
Reputation: 53000
Is it possible to use a Type as a dictionary key in Swift?
Well its possible, here is one way:
protocol Growable { ... }
struct S : Growable { ... }
class C : Growable { ... }
extension Dictionary where Key : LosslessStringConvertible
{
subscript(index: Growable.Type) -> Value?
{
get
{
return self[String(describing: index) as! Key]
}
set(newValue)
{
self[String(describing: index) as! Key] = newValue
}
}
}
var d : [String:Int] = [:]
d[S.self] = 42
d[C.self] = 24
print(d)
prints:
["C": 24, "S": 42]
If you change the subscript
definition to:
subscript(index: Any.Type) -> Value?
you can of course use any type as a key:
var d : [String:Int] = [:]
d[S.self] = 42
d[C.self] = 24
d[type(of:d)] = 18
print(d)
prints:
["C": 24, "S": 42, "Dictionary<String, Int>": 18]
I'll leave it up to you to decide whether this is wise, but its clearly possible.
[Note: you cannot constrain Key
to be String
hence the use of the protocol LosslessStringConvertible
; there might be a better choice, the Swift standard library is a moving target...]
HTH
Upvotes: 3
Reputation: 19339
You might consider taking a step back and review your design. You could model your Growable
plants using enumerations, another powerful Swift feature. For instance:
protocol Growable {
/* ... */
}
enum Vegetable: String, Hashable, Growable {
case carrot, lettuce, potato /* ... */
}
enum Mushroom: String, Hashable, Growable {
/* ... */
}
struct Farm {
var crops = [AnyHashable: Int]()
mutating func plant<T: Growable & Hashable>(qty: Int, of growable: T) {
crops[growable] = qty
}
}
Using Hashable
as a concrete type is not supported, as such, we need to use the type-erased AnyHashable
structure instead — protocols with Self or associated types requirements can be tricky to get right!
Usage:
var myFarm = Farm()
myFarm.plant(qty: 10, of: Vegetable.carrot)
myFarm.plant(qty: 20, of: Vegetable.lettuce)
myFarm.plant(qty: 30, of: Vegetable.potato)
print(myFarm.crops)
[AnyHashable(Vegetable.potato): 30, AnyHashable(Vegetable.carrot): 10, AnyHashable(Vegetable.lettuce): 20]
Hashable metatypes. Regarding your original design, the correct way to express your intent would be:
extension Growable.Type: Hashable {
/* make this meta thing hashable! */
}
i.e., making the corresponding metatype Hashable
as well, but extending metatypes isn't yet supported by Swift ;)
Upvotes: 0