generic map value

I have run into this problem a few times when wanting to use keys of maps in a similar way but the values in the maps are different. I thought I could write a function that takes the key type I want with interface{} as the value type but it doesn't work.

func main() {
    mapOne := map[string]int
    mapTwo := map[string]double
    mapThree := map[string]SomeStruct

    useKeys(mapOne)
}
func useKeys(m map[string]interface{}) {
    //something with keys here
}

Not sure if there is an elegant way to do this I just feel waist full rewriting simple things for different values.


Though maps and slices in go are generic themselves, they are not covariant (nor could they be, since interfaces aren't generics). It's part of working with a language that doesn't have generics, you will have to repeat some things.

If you really just need to get the keys of any old map, you can use reflection to do so:

func useKeys(m interface{}) {
    v := reflect.ValueOf(m)
    if v.Kind() != reflect.Map {
        fmt.Println("not a map!")
        return
    }

    keys := v.MapKeys()
    fmt.Println(keys)
}

Go 1.18

You can write a function with type parameters (generic) for this:

func useKeys[V any](m map[string]V) V {
    return m["foo"]
}

And use it as:

func main() {
    m1 := map[string]int{"foo": 1}
    m2 := map[string]float64{"foo": 4.5}
    m3 := map[string]*SomeStruct{}

    fmt.Println(useKeys(m1))
    fmt.Println(useKeys(m2))
    fmt.Println(useKeys(m3))

}

As you can see, the type parameter V unifies with the map value, so that you can explicitly force callers of useKeys to pass maps whose keys are string only.

You can see this on the GoTip Playground: https://gotipplay.golang.org/p/epFA2_9u5l5