swiftxcode10swift4.2hashable

How to make an enum conform to Hashable with the API available in Xcode 10?


In my Swift 4.2.1 code I have this enumeration:

enum MyEnum {

    case caseOne(Int)
    case caseTwo(String)
    case caseThree
}

It conforms to Equatable:

extension MyEnum: Equatable {

    static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {

        switch (lhs, rhs) {
        case (.caseOne, .caseOne), (.caseTwo, .caseTwo), (.caseThree, .caseThree):
            return true
        default:
            return false
        }
    }
}

I need to make it conform to Hashable, that's why I added an extension:

extension MyEnum: Hashable {

    var hashValue: Int {

        switch self {
        case .caseOne:
            return 1
        case .caseTwo:
            return 2
        case .caseThree:
            return 3
        }
    }
}

Now I want to migrate to the new API available in Xcode 10. I removed my implementation of hashValue and added the implementation of hash(into:):

extension MyEnum: Hashable {

    func hash(into hasher: inout Hasher) {

        switch self {
        case .caseOne:
            hasher.combine(1)
        case .caseTwo:
            hasher.combine(2)
        case .caseThree:
            hasher.combine(3)
        }
    }
}

Could you please tell me if I switched correctly to the new API? I use this test, it prints true twice if everything works fine:

var testDictionary = [MyEnum: Int]()
testDictionary[.caseOne(100)] = 100
testDictionary[.caseOne(1000)] = 1000
testDictionary[.caseTwo("100")] = 100
testDictionary[.caseTwo("1000")] = 1000
let countCaseOne = testDictionary.reduce(0) {
    if case .caseOne = $1.key {
        return $0 + 1
    }
    return $0
} == 1
print(countCaseOne) // true
let countCaseTwo = testDictionary.reduce(0) {
    if case .caseTwo = $1.key {
        return $0 + 1
    }
    return $0
} == 1
print(countCaseTwo) // true

Solution

  • You can use autogenerated Hashable conformance, as proposed in the other answer (under condition your type doesn't contains any date of non-Hashable types).

    But that's what you can do in the general case(autogenerated code would probably look like that too):

    extension MyEnum: Hashable {
    
        func hash(into hasher: inout Hasher) {
    
            switch self {
            case .caseOne(let value):
                hasher.combine(value) // combine with associated value, if it's not `Hashable` map it to some `Hashable` type and then combine result
            case .caseTwo(let value):
                hasher.combine(value) // combine with associated value, if it's not `Hashable` map it to some `Hashable` type and then combine result
            case .caseThree:
                // you can `combine` with some `Hashable` constant, but here it's ok just to skip
                break
            }
        }
    }