I have a question regarding the legality of the following statements in go. Why can't I directly convert these two types?
package main
import (
"fmt"
)
type xtype interface{}
type ytype map[string]map[string]bool
func main() {
myvar := map[string]xtype{
"x": map[string]interface{}{
"foo": map[string]interface{}{
"bar": true,
},
},
}
x := myvar["x"] // x is of type 'xtype'
fmt.Println(x) // Prints map[foo:map[bar:true]]
y := x.(ytype) // Panic
fmt.Println(y) //
}
This code compiles, but when running, you get the panic
panic: interface conversion: main.xtype is map[string]interface {}, not main.ytype
Can somebody explain why this is panics? Clearly they are of the same type in this case. Is it possible to do this kind of direct conversion in Go?
While is is a contrived example, this does come up in the real world. For example, Cloud Firestore's (part of Firebase) Go library returns maps from the database as map[string]interface{}
, no matter how many levels deep they go. So this would be really handy to convert directly into the destination type
You're trying to implicitly convert a nested interface, which doesn't work. x
is of type interface{}
, and holds, according to your structure, a map[string]interface{}
. The interfaces contained within that map then each hold a map[string]interface{}
, and those final interfaces each holds a bool. You can't convert an interface{map[string]interface{}{map[string]interface{}{bool}}
to a map[string]map[string]bool
in a single shot, because that requires unwrapping both the outer interface (the one held by x
), each of the inner interfaces in the map, and then each of the interfaces holding the bools in each of the those inner maps. Since there can be more than one key in each level of map, this is an O(n) operation (actually, closer to an O(n*m)), and interface conversions are specifically designed so you can't single-line O(n) conversions.
If you specifically unwrap each layer, and only try to unwrap a single interface at a time, it works just fine. On a side note, you can use fmt.Printf("%#v", <var>)
to print explicit type information about the variable in question.
https://play.golang.org/p/Ng9CE0O34G
package main
import (
"fmt"
)
type xtype interface{}
type ytype map[string]map[string]bool
func main() {
myvar := map[string]xtype{
"x": map[string]interface{}{
"foo": map[string]interface{}{
"bar": true,
},
},
}
x := myvar["x"] // x is of type 'xtype'
fmt.Printf("%#v
", x) // map[string]interface {}{"foo":map[string]interface {}{"bar":true}}
mid := x.(map[string]interface{})
fmt.Printf("%#v
", mid) // map[string]interface {}{"foo":map[string]interface {}{"bar":true}}
y := make(map[string]map[string]bool)
for k, v := range mid {
m := make(map[string]bool)
for j, u := range v.(map[string]interface{}) {
m[j] = u.(bool)
}
y[k] = m
}
fmt.Printf("%#v
", y) // map[string]map[string]bool{"foo":map[string]bool{"bar":true}}
}