# \[Accepted\] SE-0206: Hashable Enhancements

**URL:** <https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675>\
**Category:** Announcements\
**Created:** [April 5, 2018, 12:40am UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675 "2018-04-05T00:40:27Z")\
**Posts on this page:** 12\
**Page:** 6

<div class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [April 11, 2018, 9:49pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/106 "2018-04-11T21:49:58Z")

</div>

Minor question that I don’t see addressed in the proposal: will `Hasher` itself conform to `Hashable`?

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [April 11, 2018, 9:54pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/107 "2018-04-11T21:54:25Z")

</div>

It's not obvious to me why it would. Is there a use case you had in mind?

---

<div class="post-metadata">

**Author:** ![Braden\_Scothern](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/braden_scothern/32/1580_2.png) [@Braden\_Scothern](https://forums.swift.org/u/Braden_Scothern)\
**Post date:** [April 11, 2018, 10:01pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/108 "2018-04-11T22:01:55Z")

</div>

I really like the proposal but the one feature that I would like is a way to explicitly control the hash value given by `finalize()` rather than being forced into the underlying implementation used by `Hasher`.

The use case I see for this is when working with non-Swift tools (such as C, Python, etc.) which can have hash values. Being able to have consistent hash values of a datatype in each realm is very handy.

For example if I have this in a C header:

```swift
struct Foo {
   int bar;
   int baz;
}

int ComputeHashOfFoo(struct Foo foo);

```

I would personally want the hash value that I compute in C land to be the same as in Swift if I added an extension like this:

```swift
extension Foo: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.fix(to: Int(ComputeHashOfFoo(self)))
    }
}

```

Also, I'm not at all attached to the name `fix(to: Int)` or just a function like this being the interface to accomplish this task, it is just a quick example.

I imagine this isn't a very common use case so to discourage the use of fixing the value it should probably be named something like `unsafelyLockFinalizedValue(to: Int)` or something along those lines. My brain is a little too fried to think of better naming at the moment...

---

<div class="post-metadata">

**Author:** ![felix91gr](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/felix91gr/32/443_2.png) [@felix91gr](https://forums.swift.org/u/felix91gr)\
**Post date:** [April 11, 2018, 10:26pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/109 "2018-04-11T22:26:28Z")

</div>

> [@Nevin](#):
>
> Minor question that I don’t see addressed in the proposal: will Hasher itself conform to Hashable?

It could, and it would be cool, but you’d need something that differentiates two Hashers in a way that is prone to hashing.  
This could be accomplished by extracting a representation of each Hashers’ hashing algorithm using some advanced Reflection feature. Then, you’d feed that representation to a Hasher and get their hash values.

I don’t think something like that exists in Swift atm, but damn that would be cool to make someday. A Hashtable with Hashers as keys? Hell yeah, why not :D

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [April 12, 2018, 10:03pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/110 "2018-04-12T22:03:13Z")

</div>

I wanted to raise a couple of questions about the design of `finalize` that came up in discussions yesterday:

First of all, a recurring theme in this discussion was the `finalize` method and the fact that, as proposed, it invalidates the `Hasher` value it's invoked on so that further combine operations trap. There's a straightforward answer to this problem in the language today: `finalize` could be made into a nonmutating `consuming` method. In Swift today, that would prevent the `finalize` operation from invalidating the hasher, since it would receive a copy of the hasher state instead of modifying the hasher in-place. Improperly invoking `finalize` on an `inout` hasher argument would give you a hash value based on the current hasher state up to this point, but not invalidate the hasher for future use. Looking forward to the proposed ownership model for Swift, a `hash(into:)` implementation could mark the `hasher` argument as move-only (and maybe `Hasher` itself could be made move-only or explicit-copy-only conditional on Swift language version), making so that you wouldn't be _allowed_ to `finalize` during a `hash(into:)` operation, since you can't consume an inout-borrowed value. It's debatable whether silently providing an incomplete hash value is a _better_ failure mode than trapping after improper use of `finalize` in the short term, but this design choice could lead to the correct long-term behavior further down in Swift's roadmap.

Next, I'd like to suggest the idea of using a stronger return type for the return type of `finalize`. @lorentey pointed out a potential problem if in the future we added support for one-shot hashing, since a one-shot `hashOnly` has the potential to be misimplemented by ignoring its hasher and simply returning a fixed value:

```swift
struct MyType: Hashable {
  func hashOnly(into hasher: Hasher) -> Int {
    // We ought to use the hasher to generate a hash so that we factor in its seed,
    // but nothing's forcing us to:
    return 1738
  }
}

```

This is even _worse_ than `hashValue` since we'd expect `hashOnly` to return a pre-seeded value, so the raw hash value would be accepted blindly instead of getting mixed in with the seed like a plain `hashValue` would. We could use the type system to direct use here, by wrapping up the final hash value in a struct that only the standard library `Hasher` can construct:

```swift
@fixedLayout public struct HashValue {
  public let value: Int

  // Only usable by Hasher. Could be public API with a sufficiently scary name?
  fileprivate init(_rawHashValue: Int) { self.value = _rawHashValue }
}

```

That way, `hashOnly` or another API that is intended to return a strong hash value can return the `HashValue` type instead of a plain `Int` to ensure that the result in fact came from a `Hasher`.

---

<div class="post-metadata">

**Author:** ![hlovatt](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hlovatt/32/3044_2.png) [@hlovatt](https://forums.swift.org/u/hlovatt)\
**Post date:** [April 12, 2018, 10:58pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/111 "2018-04-12T22:58:41Z")

</div>

@lorentey Thanks for commenting.

> [@lorentey](#):
>
> It may seem like a surface-level change to you

I do consider the change I suggested as a surface change, primarily because it retains the innovative step of providing the hasher instead of receiving a hash value.

> [@lorentey](#):
>
> The new hash(into:) and (especially!) HashCache would also very likely not benchmark the way you expect.

Actually I expect them currently to be slower because of retain-release cycles, however once we get ownership manifesto and can mark the hasher as `@nonescaping` (or whatever it is called) then I would expect that like in other languages the cost of passing a class will be lower than passing a largish struct.

> [@lorentey](#):
>
> We did carefully consider making Hasher a protocol. ... There may be a use case for custom Hashers, but it hasn’t come up in review discussions so far.

I can think of lots of uses for custom hashers:

1. Often implementations of Bloom filters Provide many different hashing functions, e.g. [GitHub - Baqend/Orestes-Bloomfilter: Library of different Bloom filters in Java with optional Redis-backing, counting and many hashing options.](https://github.com/Baqend/Orestes-Bloomfilter) provides 10+.
2. You may well want to know exactly what hasher is used, for example many Bloom Filter implementations choose Murmur as the best compromise between speed and performance.
3. A library of Hashers could, maybe not at first but latter on, contain common message hashers like MD5 and SHA-1.

> [@lorentey](#):
>
> The Bloom filter you’ve shown does not utilize multiple Hasher implementations, so it argues more against generalizing Hasher than in favor of it.

The Bloom Filter I wrote was just an example, I don't consider it serious code. I wanted to demonstrate that the proposed API would enable more exotic use of hashers whilst still enabling Caching and not requiring tons of boilerplate code. As noted above there are many useful hashers.

> [@lorentey](#):
>
> The idea to partially automate hash caching is intriguing, but it seems like it is orthogonal to this proposal, and it can be built on top of it later.

Yes it could be a seperate proposal.

> [@lorentey](#):
>
> Note that Bloom64 allocates a shockingly large amount of space to cache hash values; I believe this goes directly against the spirit of bloom filtering, and some of that space would be much better used as additional state space for the filter itself.

Two points:

1. The caching shown is far from optimal to keep the post short, as noted in the comments. In practice a SortedDictionary would perform better than the example's Dictionary. (But SortedDictionary doesn't exist yet!)
2. The extra space is only for caching if nothing caches then the overhead would be small (see point 1). If something is cached then yes you are trading of space for speed, but that is what the programmer has decreed worthwhile.

In summary: I think the main point is the restructuring to enable future growth and to anticipate the implementation of the ownership manifesto and I agree that the caching could be seperate.

Thanks for putting all your work into your proposal, it is a really worthwhile improvement to Swift.

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [April 18, 2018, 3:46pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/112 "2018-04-18T15:46:18Z")

</div>

> [@Joe\_Groff](#):
>
> Next, I’d like to suggest the idea of using a stronger return type for the return type of finalize. @lorentey pointed out a potential problem if in the future we added support for one-shot hashing, since a one-shot hashOnly has the potential to be misimplemented by ignoring its hasher and simply returning a fixed value:

After sleeping on this, I'm getting increasingly convinced we should just embrace that one-shot hashing provides an unsafe escape hatch from `Hasher`:

```swift
protocol Hashable {
  func hash(into hasher: inout Hasher)
  func unsafeHashValue(seed: (UInt64, UInt64)) -> Int
}

extension Hashable {
  public func unsafeHashValue(seed: (UInt64, UInt64)) -> Int {
    var hasher = Hasher(seed: seed)
    self.hash(into: &hasher)
    return hasher.finalize()
  }
}

```

In addition to one-shot hashing, this would also allow people to experiment with new hash functions without having to change the stdlib. (`unsafeHashValue` is essentially the same as the `hashValue` property, but with a seed.)

(To be clear, we should not do this now. Adding this as public API would be a separate proposal. But introducing a `HashValue` type now may turn out to be overly cautious later.)

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [April 18, 2018, 4:09pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/113 "2018-04-18T16:09:20Z")

</div>

Fair enough. I suppose this doesn't make much difference if it isn't public API to begin with, but would it make sense to pass a pre-seeded `Hasher` in instead of the seed value itself, or is it useful for the implementer to be able to take full control of hashing?

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [April 18, 2018, 4:31pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/114 "2018-04-18T16:31:44Z")

</div>

Passing in the seed is more geared towards the "escape hatch" use case rather than one-shot hashing. (An operation to extract a seed from `Hasher` would make a messy public API.) As long as we only want to enable one-shot hashing, there is no reason to expose seed values.

That said, in the current version of the (non-public) one-shot hashing implementation, it was useful to collapse all `Hasher` operations into a single resilient call, including its initialization. Eliminating the extra noninlinable function call makes a measurable improvement for tiny keys. This will change once Set/Dictionary stores full local Hasher instances rather than just their local seeds.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [April 18, 2018, 7:38pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/115 "2018-04-18T19:38:04Z")

</div>

The core team has **accepted** this proposal with the following revisions:

- As @jrose noted, init(seed:)'s behavior may be misleading to users who expect it to enable deterministic hashing, and has no real benefit over a collection implementation seeding the hasher directly with combine calls. To avoid misleading users, and to simplify the interface, we strongly suggest not including init(seed:) in the public Hasher API, instead suggesting that hashed collection implementers seed their hashers manually.

- As a further simplification, the core team observes that the many combine(bits:) overloads on Hasher that take fixed-sized integers don’t need to be public API. Instead, they can be @usableFromInline entry points invoked by @inlinable implementations of hash(into:) on the integer types themselves:

- The core team recommends that `Hasher.finalize()` be made a `__consuming` nonmutating method instead of a mutating method that invalidates the `Hasher` value. This saves some overhead from `Hasher` having to check its validation state, and looking forward to a future version of Swift with an ownership model and support for move-only values, would allow correct use of `finalize()` to be statically enforced on move-only `Hasher` instances.

With the exception of the last point, these were the same revisions suggested by the core team in [our previous review](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/104) of the proposal. In further study, @lorentey discovered that there is significant performance value to providing a one-shot hashing interface for standard library types, particularly pure-ASCII `String`s, though the benefit for user-defined types will be less due to resilience. Therefore, while the implementation may use one-shot hashing as an implementation detail, the core team does not see the need to design a public API for this functionality at this time.

Thanks again to everyone for participating in the review!

---

<div class="post-metadata">

**Author:** ![reitzig](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/reitzig/32/2054_2.png) [@reitzig](https://forums.swift.org/u/reitzig)\
**Post date:** [April 24, 2018, 12:24pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/116 "2018-04-24T12:24:21Z")

</div>

I'm a little late to the party and slightly offtopic (applies to `Equatable` as well), but still: it would be great if the compiler error message could be improved in case auto-synthesizing fails. Currently, it only outputs "does not conform to protocol 'Hashable'"; it could inform us about _why_ in a note. Typical cases include "property/associated value X is not `Hashable` so I could not synthesize".

But then, the compiler doesn't even tell us which protocol requirement is unfulfilled, so...

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [April 24, 2018, 4:23pm UTC](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/117 "2018-04-24T16:23:21Z")

</div>

Thanks @reitzig. If you notice deficiencies in diagnostics, please file bugs at [bugs.swift.org](http://bugs.swift.org), or start a new thread under the "Users" tag to discuss them. This review thread is completed, and it's off-topic to discuss implementation issues here.

[Previous page](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675.md?page=5)
