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:
extension Hasher { @usableFromInline internal mutating func _combine(bits: UInt8) /* etc. */ } extension UInt8: Hashable { @inlinable public func hash(into hasher: inout Hasher) { hasher._combine(bits: self) } }
This greatly reduces the public API surface area of Hasher, in particular eliminating the large overload set for combine(bits:), while preserving the performance characteristics of the proposed design, since using the generic combine<H: Hashable> method with a fixed-sized integer type would naturally inline down to a call to the corresponding @usableFromInline entry point. This would reduce the public Hasher API to only init(), combine<H: Hashable>(_: H), the combine(bits:) method that takes a raw buffer of hash input, and finalize() as public API.
-
The core team recommends that
Hasher.finalize()
be made a__consuming
nonmutating method instead of a mutating method that invalidates theHasher
value. This saves some overhead fromHasher
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 offinalize()
to be statically enforced on move-onlyHasher
instances.
With the exception of the last point, these were the same revisions suggested by the core team in our previous review 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!