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!