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