Conforming to Hashable protocol?
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
}
}
}
Related videos on Youtube
Comments
-
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 about 7 yearsThankyou! Btw, why is the
==
wrong? Wouldn't 2 dates with the same day, month, and year have the same hashValue anyways? -
rmaddy about 7 yearsOf 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 about 7 yearsWell 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 about 7 yearsYour implementation of
hashValue
is fine. It would be correct to simply return 42 if you wanted (but please don't). From theHashable
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 about 7 yearsOh I see, so the combination of the
hashValue
and==
makes comparing work. Thanks! -
rmaddy about 7 yearsNot 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 thatHashable
extendsEquatable
. 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 about 7 yearsOh, so thats why if I say
dict[DateStruct1] = 1
anddict[DateStruct2] = 2
it wouldn't be accessing the same dictionary entry even ifDateStruct1
andDateStruct2
might have the same hashValue, it would also compare them using==
. -
Jeremy Caney almost 4 yearsWhat'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 almost 4 yearsThis 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 about 2 yearsSwift 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)