A Wrapper Pattern for Custom Dictionary Keys

In Swift, the Dictionary type is defined as a struct where the keys conform to the Hashable protocol: public struct Dictionary<Key : Hashable, Value>.


For a type to be “hashable” it must have a hashValue property that is consistent with the equality comparison. In other words, if two values of a “hashable” type are equal, their hashValues must also be equal.


If your dictionary keys are of a non-hashable type (for instance, a tuple containing enum values), tyour code won’t compile.


enum State: Int { case s1, s2, s3, s4 }

enum Value: Int { case B, X }

let myDict: Dictionary<(Value, State), [Any]> // error: does not conform to Hashable

Rather than choose a different type, you can wrap your custom type in a generic struct that implements Hashable. In the example above (a tuple containing enum values), you must also make sure that your enums are backed by a type that implements Hashable (for instance Int).


struct ValueStatePair<ValueType: Hashable, StateType: Hashable>: Hashable {

    let pair: (ValueType, StateType)

    var hashValue: Int {
        get {
            let (a, b) = pair
            return a.hashValue &* 31 &+ b.hashValue
        }
    }
    
}

// Types implementing to Hashable must also implement Equatable
extension ValueStatePair: Equatable {}

func ==<ValueType: Hashable,
        StateType: Hashable>(lhs: ValueStatePair<ValueType, StateType>,
        rhs: ValueStatePair<ValueType, StateType>) -> Bool {
    return lhs.pair == rhs.pair
}

Using this wrapper, constructing a Dictionary with custom key types is a snap:


let myDict: Dictionary<ValueStatePair<Value, State>, Int> = [
    ValueStatePair(pair: (.B, .s1)): 0,
    ValueStatePair(pair: (.X, .s2)): 1
]