I want to set multiple types for values of hashmap in Golang. I implemented golang generics any
, and wrote function that returned map[string]any
.
However, after I ran the code, it returned
$ cannot use r.Method (variable of type string) as type T in map literal
What is the correct way of setting multiple types for values of hashmap in Go?
Here's my code
package main
type RequestProperty struct {
Method string
Params []any
Id int
}
func SetRequestProperty[T any](payloadCombine bool) map[string]T {
var p map[string]T
var r = RequestProperty{
Method: "SET_PROPERTY",
Params: []any{"combined", payloadCombine},
Id: 5,
}
// just for test
p = map[string]T{
"method": r.Method, // << Error Here
}
return p
}
func main() {
p := SetRequestProperty(true)
}
[EDIT] This seems to be working though... I don't know why.
package main
type RequestProperty struct {
Method string
Params []any
Id int
}
// delete [T any], map[string]T
// change it to map[string]any
func SetRequestProperty(payloadCombine bool) map[string]any {
var p map[string]any
var r = RequestProperty{
Method: "SET_PROPERTY",
Params: []any{"combined", payloadCombine},
Id: 5,
}
// just for test
p = map[string]any{
"method": r.Method,
}
return p
}
func main() {
p := SetRequestProperty(true)
}
Shouldn't T
just act like an alias to type any? Am I misunderstanding something?
CodePudding user response:
Shouldn't T just act like an alias to type any?
No, it shouldn't.
T
is a type parameter, not any
. It is only constrained by any
.
More generally: a type parameter is not its constraint.
Each time a generic function is instantiated, T
is assigned a concrete type argument — which satisfies its constraint — and within the function body, map[string]T
becomes a map from string
to whatever the concrete T
is.
p := SetRequestProperty[int](true)
// makes body look like: `var p map[string]int`
// the literal `map[string]int{"method": r.Method}` obviously can't work
Therefore at compile time, the compiler will reject assignments to T
that are not compatible with all types in T
's type set. The code map[string]T{"method": r.Method}
doesn't compile because:
T
is constrained byany
, so its type set comprises anythingr.Method
is of typestring
, andstring
isn't assignable to anything.
With map[string]any
instead any
is not used as a constraint, it is used as a static type, which is an alias of interface{}
and all types are always assignable to the empty interface.
If you want to have a container with different runtime types, using any
as a static type as in map[string]any
is the only way. To restrict allowed types, use basic interfaces instead of type parameters.
See also the top-voted answer here: What are the benefits of replacing an interface argument with a type parameter?