In a StackOverflow comment, there is some confusion about how to hash the components of the value being hashed.
The Hashable documentation says the function should look like
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(name)
hasher.combine(location)
}
However, the WWDC 2018 What’s New in Swift session suggests the function should look like
func hash(into hasher: inout Hasher) {
id.hash(into: &hasher)
name.hash(into: &hasher)
location.hash(into: &hasher)
}
Two questions:
- Is there a functional difference between the two?
- Which one is recommended or more idiomatic?
1 Like
lorentey
(Karoy Lorentey)
3
Hasher's generic combine method is defined in Hasher.swift to simply call hash(into:) on its argument:
@inlinable
@inline(__always)
public mutating func combine<H: Hashable>(_ value: H) {
value.hash(into: &self)
}
So hasher.combine(foo) is in fact just an alternative spelling for foo.hash(into: &hasher). They are completely equivalent. Choosing one or the other is a matter of aesthetic preference.
I personally prefer to use the generic combine, because it reads clearer to me. (I find that the inout argument makes hash(into:) rather noisy -- it's a sort of speed bump that makes my brain pause a bit whenever I see it.) This is why I left combine<H>(_:) in the proposal after we removed most of the other combine overloads. However, I can also see reasons for using hash(into:) directly, like Slava did in the WWDC session; after all, hasher.combine(_:) is one more thing you need to learn.
That said, while I do like combine(_:) better, I don't really like to use either form. I overwhelmingly prefer to just let the compiler implement hash(into:) for me automatically. I'd love to do that even more often!
8 Likes