4

According to the reflect documentation reflect.Value.MapIndex() should return a reflect.Value which represents the Value of the data stored at a specific key of a map. So my understanding would be that the following two expressions should be identical. In the first case we are getting the result from MapIndex(). In the second we are getting the result from MapIndex() getting it's underlying data and then doing a reflect.ValueOf() on that.

reflect.ValueOf(map).MapIndex("Key")
reflect.ValueOf(reflect.ValueOf(map).MapIndex("Key").Interface())

Why is the additional reflect.ValueOf() required?

Example Code:

package main

import "fmt"
import "reflect"

func main() {
    test := map[string]interface{}{"First": "firstValue"}
    Pass(test)
}

func Pass(d interface{}) {
    mydata := reflect.ValueOf(d).MapIndex(reflect.ValueOf("First"))
    fmt.Printf("Value: %+v \n", mydata.Interface())
    fmt.Printf("Kind: %+v \n", mydata.Kind())
    fmt.Printf("Kind2: %+v \n", reflect.ValueOf(mydata.Interface()).Kind())
}

Go Play: http://play.golang.org/p/TG4SzrtTf0

2 Answers 2

2

After thinking about this for a while this falls under the duh category. It has to do with the nature of interfaces in Go, which are reference objects which point to other things. I have explicitly declared my map to be map[string]interface{} meaning that the value at the location of each key is an interface{}, not a string, so I really shouldn't be surprised to receive a reflect.Value which holds an interface{}.

The additional reflect.ValueOf() dives one layer deeper to gain the underlying Value of the interface{}. I created two examples, both of which I believe confirm this behavior.

Example using a map[string]Stringer custom interface: http://play.golang.org/p/zXCn9Fce3Q

package main

import "fmt"
import "reflect"

type Test struct {
    Data string
}

func (t Test) GetData() string {
    return t.Data
}

type Stringer interface {
    GetData() string
}

func main() {
    test := map[string]Stringer{"First": Test{Data: "testing"}}
    Pass(test)
}

func Pass(d interface{}) {
    mydata := reflect.ValueOf(d).MapIndex(reflect.ValueOf("First"))
    fmt.Printf("Value: %+v \n", mydata.Interface())
    fmt.Printf("Kind: %+v \n", mydata.Kind())
    fmt.Printf("Kind2: %+v \n", reflect.ValueOf(mydata.Interface()).Kind())
}

returns

Value: {Data:testing} 
Kind: interface 
Kind2: struct

Example using a map[string]string: http://play.golang.org/p/vXuPzmObgN

package main

import "fmt"
import "reflect"

func main() {
    test := map[string]string{"First": "firstValue"}
    Pass(test)
}

func Pass(d interface{}) {
    mydata := reflect.ValueOf(d).MapIndex(reflect.ValueOf("First"))
    fmt.Printf("Value: %+v \n", mydata.Interface())
    fmt.Printf("Kind: %+v \n", mydata.Kind())
    fmt.Printf("Kind2: %+v \n", reflect.ValueOf(mydata.Interface()).Kind())
}

returns

Value: firstValue 
Kind: string 
Kind2: string 
1
  • Whups and you answered while I was typing! :) Commented Jan 4, 2013 at 4:46
1
func Pass(d interface{}) {
    mydata := reflect.ValueOf(d).MapIndex(reflect.ValueOf("First"))
    fmt.Printf("Value: %+v \n", mydata.Interface())
    fmt.Printf("Kind: %+v \n", mydata.Kind())

At this point in your program, mydata is an interface, so no surprises that Go reports it as such when Kind() is called.

    fmt.Printf("Kind2: %+v \n", reflect.ValueOf(mydata.Interface()).Kind())

Break this down:

s := mydata.Interface()  // s is a string
v := reflect.ValueOf(s)  // v is a reflect.Value
k := v.Kind()            // k is a reflect.Kind "string"

I think you may be being tripped up by the fact that your map contains interfaces, not strings.

Not the answer you're looking for? Browse other questions tagged or ask your own question.