Hashable Protocols in Swift
Yesterday I had a conversation about sets and the idea of types conforming to a hashable protocol came up. It didn’t take long for me realize that my understanding of hashable protocols were limited, so I started piecing together the information that I did know. I knew that a protocol is similar to a blueprint in that it lays out a number of responsibilities that one must adopt and conform to. I also knew that sets stored distinct values of the same type in an unordered collection. My guess is that when we are using a custom type in a set, it has to conform to hashable protocol, so that means that there are a number of requirements it needs to meet in order to be considered hashable. Either way, I couldn’t go in depth about what a hashable protocol actually was or how they were used in custom types, so I began to do some investigating.
I headed over to Apple’s documentation to try and figure out what a hashable protocol was and hopefully get a more complete picture of why they are used in sets.
Apple defines hashable as “a type that provides an integer a hash value.” Okay, but what’s a hash value?- With some more reading I found that a hash value is provided by a type’s hashValue
property and it is basically an integer that is the same for any two instances that compare equally.
To sum that up, a hashable
is a type that has hashValue
in the form of an integer that can be compared across different types.
So let’s say for instance that we have two instances, a
and b
. If a == b
, then a.hashValue == b.hashValue
, however two instances with the same hashValue
will not always be equal.
When we conform to a hashable protocol we must also conform to the equatable protocol, but let’s take this one step at a time.
We conform to a hashable protocol like we do any other protocol:
class Dog: Hashable { var uid: Int
var name: String
var age: Int init(uid: Int, name: String, age: Int) {
self.uid = uid
self.name = name
self.age = age
}
}
But when we conform to a hashable protocol we must have a hashValue
property.
class Dog: Hashable { var uid: Int
var name: String
var age: Int
var hashValue: Int {
return uid.hashValue
}init(uid, Int, name: String, age: Int) {
self.uid = uid
self.name = name
self.age = age
}
}
If we leave our code this way you’ll notice an error: “Type ‘Dog’ does not conform to protocol ‘Equatable’.”
Although we are conforming to Hashable
, Hashable
must also conform to Equatable
. Apple definesEquatable
as a type that can be compared for value equality, which if we remember, is part of the working definition for a hashable protocol.
To conform to Equatable
we must add a function that compares the type and checks to see if they are equal.
static func ==(lhs: Dog, rhs: Dog) -> Bool {
return lhs.uid == rhs.uid
}
And just like that we can begin to use our custom types as key values for dictionaries and or elements in a set too!
let dog1 = Dog(uid: 1, name: "Scrappy", age: 4)
let dog2 = Dog(uid: 2, name: "Sparky", age: 3)var dogArray: [Dog: String] = [
dog1: "Woff Woff",
dog2: "Wooooooof!"
]var dogs: Set<Dog> = [dog1, dog2]
You will also notice that is we compare each instance’s hashValue
they are different even though they are of the same type.
print(dog2.hashValue)
print(dog1.hashValue) // prints 4792065050360486817
// prints 8918364877838387272
To conclude, a hashable protocol allows us to create custom types that can be compared for it’s equality using it’s hashValue
. In this way we are able to use our custom type in a set or as a key in a dictionary while ensuring that it has a unique value.
Thanks for reading!