'Conforming to Hashable protocol?

I'm trying to make a dictionary with the key as a struct I've created and the value as an array of Ints. However, I keep getting the error:

Type 'DateStruct' does not conform to protocol 'Hashable'

I'm pretty sure I've implemented the necessary methods but for some reason it still doesn't work.

Here's my struct with the implemented protocols:

struct DateStruct {
    var year: Int
    var month: Int
    var day: Int

    var hashValue: Int {
        return (year+month+day).hashValue
    }

    static func == (lhs: DateStruct, rhs: DateStruct) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }

    static func < (lhs: DateStruct, rhs: DateStruct) -> Bool {
        if (lhs.year < rhs.year) {
            return true
        } else if (lhs.year > rhs.year) {
            return false
        } else {
            if (lhs.month < rhs.month) {
                return true
            } else if (lhs.month > rhs.month) {
                return false
            } else {
                if (lhs.day < rhs.day) {
                    return true
                } else {
                    return false
                }
            }
        }
    }
}

Can anybody please explain to me why I'm still getting the error?



Solution 1:[1]

    var hashValue: Int 

is obsolete (except in the legacy NSObject inheritance trees).

    func hash(into hasher: inout Hasher)
    {
        hasher.combine(year);
        hasher.combine(month) 
    ...

is the swiftlely modern way to hash your way in class.

And per rmaddy answer above "==" operator also has to have kinks ironed out to be correct for your semantics.

Per Manish you get Hashable comformance for structs for free just declaring that.

Solution 2:[2]

If you don't want to use the hashValue, you can combine the hash of your values with the hash(into:) method.

For more information see the answer : https://stackoverflow.com/a/55118328/1261547

Solution 3:[3]

If the class has fields of type (another class), that class should adopt Hashable.

example

struct Project : Hashable {
    var activities: [Activity]?

}

Here, Activity class must adopt Hashable too.

Solution 4:[4]

For simple structs, where all its properties are already Hashable (i.e. Int, String, ... ), we can conform to Hashable just declaring it (see https://developer.apple.com/documentation/swift/hashable )

So no need to implement hashValue (which btw is deprecated) nor == (because Hashable conforms to Equatable).

And since we're implementing the < operator, then it'd make sense to conform to Comparable, so we can sort (i.e. [dateStructA, dateStructB, ...].sorted()).

So I'd do it like:

struct DateStruct: Comparable & Hashable {
    let year: Int
    let month: Int
    let day: Int

    static func < (lhs: DateStruct, rhs: DateStruct) -> Bool {
        if lhs.year != rhs.year {
           return lhs.year < rhs.year
        } else if lhs.month != rhs.month {
           return lhs.month < rhs.month
        } else {
           return lhs.day < rhs.day
        }
    }
}

Solution 5:[5]

You did not specified the Hashable protocol when defining struct:

struct DateStruct: Hashable { ...

The following code is from your example and it runs on a Playground. Please note that your == operator has been modified here:

import Foundation

struct DateStruct: Hashable {
    var year: Int
    var month: Int
    var day: Int

    var hashValue: Int {
        return (year+month+day).hashValue
    }

    static func == (lhs: DateStruct, rhs: DateStruct) -> Bool {
        return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day
    }

    static func < (lhs: DateStruct, rhs: DateStruct) -> Bool {
        if (lhs.year < rhs.year) {
            return true
        } else if (lhs.year > rhs.year) {
            return false
        } else {
            if (lhs.month < rhs.month) {
                return true
            } else if (lhs.month > rhs.month) {
                return false
            } else {
                if (lhs.day < rhs.day) {
                    return true
                } else {
                    return false
                }
            }
        }
    }
}

var d0 = DateStruct(year: 2017, month: 2, day: 21)
var d1 = DateStruct(year: 2017, month: 2, day: 21)

var dates = [DateStruct:Int]()
dates[d0] = 23
dates[d1] = 49

print(dates)

print(d0 == d1) // true

d0.year = 2018

print(d0 == d1) // false

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Lilo
Solution 3 Hatim
Solution 4 FranMowinckel
Solution 5 pkamb