I had a function implemented to obtain the keys in a map (a few versions actually, for different types), which I updated to use generics in Go 1.18. Then I found out that the experimental library was extended to include that functionality, and while my implementation was nearly identical, the function declaration has some differences that I would like to better understand.
Here is my original generic version (I renamed the variables to match the standard library, to better highlight was is actually different):
func mapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
And here is the standard-library version:
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
As you can see, the main difference is the extra M ~map[K]V type parameter, which I omitted and directly used map[K]V for the function's argument type. My function works, so why would I need to go through the extra trouble of adding a third parameterized type?
As I was writing my question, I thought I had figured out the answer: to be able to invoke the function on types that are really maps under the covers, but are not directly declared as such, like maybe on this DataCache type:
type DataCache map[string]DataObject
My thinking was that this probably required the ~map notation, and the ~ can only be used in a type constraint, not in an actual type. Only problem with this theory: my version works fine on such map types. So I am at a loss as to what it's useful for.