Conforming to Hashable protocol?

43,638

Solution 1

You're missing the declaration:

struct DateStruct: Hashable {

And your == function is wrong. You should compare the three properties.

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

It's possible for two different values to have the same hash value.

Solution 2

    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 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

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 5

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
        }
    }
}
Share:
43,638

Related videos on Youtube

MarksCode
Author by

MarksCode

Student

Updated on July 09, 2022

Comments

  • MarksCode
    MarksCode almost 2 years

    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?

  • MarksCode
    MarksCode about 7 years
    Thankyou! Btw, why is the == wrong? Wouldn't 2 dates with the same day, month, and year have the same hashValue anyways?
  • rmaddy
    rmaddy about 7 years
    Of course two equal dates will have the same hash. But two dates with the same hash are not necessarily equal. Two different dates can have the same hash. That's fine. But two dates with the same hash don't have to be equal.
  • MarksCode
    MarksCode about 7 years
    Well Im confused, anyways is my return (year+month+day).hashValue not sufficient since 2 different date's year, month, and day could add up to the same thing like 2000, 1, 2 and 2000, 2, 1?
  • rmaddy
    rmaddy about 7 years
    Your implementation of hashValue is fine. It would be correct to simply return 42 if you wanted (but please don't). From the Hashable docs: "A hash value, provided by a type’s hashValue property, is an integer that is the same for any two instances that compare equally. That is, for two instances a and b of the same type, if a == b then a.hashValue == b.hashValue. The reverse is not true: Two instances with equal hash values are not necessarily equal to each other." That's why you need to fix your == function.
  • MarksCode
    MarksCode about 7 years
    Oh I see, so the combination of the hashValue and == makes comparing work. Thanks!
  • rmaddy
    rmaddy about 7 years
    Not really. == makes comparing work. You can have comparison without hashing. hashValue makes values "hashable" which allows them to be used as dictionary keys or to be stored in some other form of a hash map/table. Note that Hashable extends Equatable. This means that hashing depends on comparing but comparing does not depend on hashing. Read the docs for those two protocols for further details.
  • MarksCode
    MarksCode about 7 years
    Oh, so thats why if I say dict[DateStruct1] = 1 and dict[DateStruct2] = 2 it wouldn't be accessing the same dictionary entry even if DateStruct1 and DateStruct2 might have the same hashValue, it would also compare them using ==.
  • Jeremy Caney
    Jeremy Caney almost 4 years
    What's the benefit of this approach over e.g. @rmaddy's accepted answer from three years ago? Under what conditions might someone prefer your suggestion?
  • Anton Tropashko
    Anton Tropashko almost 4 years
    This is not a question of a benefit: maddy answer is required but not sufficient to satisfy Hashable requirements. Once you add == the error remains but xcode 11.3 through 11.5 would NOT offer you to create protocol stub for hash(into
  • Manish Nahar
    Manish Nahar about 2 years
    Swift will generate a hash(into:) method automatically if the struct conforms to the Hashable protocol and all properties also conform to the Hashable protocol (Swift 5.4 and Xcode 13.2.1)