Description
Background
The Go runtime contains an efficient hash function that it uses to implement built-in maps.
The runtime hash function is capable of hashing arbitrary comparable values, and usually cannot be pruned away during linking: for example, it would be very difficult for the linker to determine which hash functions are needed in a program that uses reflect.MapOf
to construct maps at run-time, or a program that stores interface{}
values in a map[interface{}]Anything
.
Occasionally, we need an efficient hash function outside the standard library, too: for example, we might want to implement other hash-based data structures, such as hash array mapped tries, concurrent tries, or prototypes for potential additions to the Go standard library (e.g. sync.Map), or write application code to compute hashes for normally-incomparable types (such as slices or maps) to be able to store them in built-in maps. (For the latter case, a built-in hash function would be a building block for a larger application-level hash function.)
Typical workarounds include:
- pushing computation of hashes to application code
http://godoc.org/github.com/apmckinlay/gsuneido/util/hmap#Key
http://godoc.org/github.com/hit9/htree#Item
http://godoc.org/github.com/lleo/go-hamt-key#Key
http://godoc.org/github.com/Workiva/go-datastructures/trie/dtrie#New - restricting application code to strings, byte-slices, integers, or pointers:
http://godoc.org/github.com/Workiva/go-datastructures/trie/ctrie#Ctrie.Insert
http://godoc.org/github.com/lalonde/hamt#Key
https://github.com/crawshaw/readmap/blob/master/readmap.go
http://godoc.org/github.com/orcaman/concurrent-map#ConcurrentMap.Set
http://godoc.org/github.com/OneOfOne/cmap#CMap.Set - or, occasionally, using
reflect
orunsafe
to reimplement arbitrary hashing in application code:
http://godoc.org/github.com/dlsniper/anyhash
http://godoc.org/github.com/mitchellh/hashstructure
http://godoc.org/github.com/cnf/structhash
I believe that the toil involved in implementing these workarounds discourages experimentation with more efficient data structures in Go, and needlessly complicates application code.
It would be reckless for us to expose a stable hash function in the standard library. However, it seems like it would be useful to expose a per-process hash function, randomly salted at each invocation of the process, in order to facilitate the use of custom data structures in user code.
Proposal
I propose that we add the following function to the hash
package:
// Local returns a hash code for a comparable argument using a hash function
// that is local to the current invocation of the program.
//
// Two values that compare equal using the == operator (and values that are
// unequal but have the same type and representation in memory, such as
// floating-point NaNs with the same bitwise representation) will have the same
// hash code. Values that compare unequal will have a high probability of
// returning different hash codes.
//
// Local panics if the argument is not comparable.
//
// Each call to Local with the same value will return the same result within the
// lifetime of the program, but each run of the program may return different
// results from previous runs.
func Local(interface{}) uintptr