Reflect thinks struct Value is also a ptr?

2019-05-14 20:41发布

问题:

I have a data structure like this demo. As you can see, foo has an embedded pointer to bar:

type foo struct {
    *bar
}

type bar struct {
    S []byte
}

And I'm using the reflect package like this:

func test(x interface{}) {

    var v = reflect.ValueOf(x)

    if v.Kind() == reflect.Struct {
        fmt.Println("was a struct")

    // panic: reflect: call of reflect.Value.Elem on struct Value
    //  v = v.Elem()

    // panic: reflect: call of reflect.Value.Field on ptr Value
        v = v.FieldByName("S")
    }
}

func main() {
    var f foo
    test(f)
    fmt.Println(string(f.S))
}

So v.Kind() is recognized as a reflect.Struct, but if I try to treat it like a struct by using .FieldByName("S"), it panics because it thinks v is a ptr.

So then if I try to treat it like a ptr by calling .Elem(), it panics because it thinks v is a struct.

I've tried reflect.Indirect(), as well as a few other things, but I can't figure out how to get the field of an embedded pointer.

Is there a way to get the reflect.Value representation from an embedded pointer to a struct?

Demo: http://play.golang.org/p/n0eea6XW3I


EDIT: Also tried v = v.FieldByName("bar"), but got:

panic: runtime error: invalid memory address or nil pointer dereference

回答1:

The first thing we need to realize is that the line var f foo is equivalent to f := foo{}. This initializes the internal field bar (of type *bar) to its zero value... nil. The behavior of embedded types and reflect seems to be that it treats the embedded type's fields as fields of the type itself. So when you request v.FieldByName("S") it's trying to find that field in f's member, bar, which is nil.

You're trying to do this (*f.bar).S. (In Go the explicit pointer dereference isn't needed, but it makes my point). Now the question is: if you change is to v.FieldByName("bar") why does it give an error? Same reason.

Look closely at the stack trace, the FieldByName line no longer crashes, the line that crashes is fmt.Println(string(f.S)). Again, semantically you're doing (*f.bar).S. But the member "bar" is nil, so you are, in fact, doing a nil pointer dereference.

You can fix both errors by changing var f foo to f := foo{&bar{}}.



回答2:

i was getting this error " panic: reflect: call of reflect.Value.Elem on struct Value" bcz of this line "reflect.ValueOf(parameterName).Elem()"

1.When i am using Elem() in reflex ,it means parameterName inside valueOf() should be a pointer to structure

func Search(flight interface{}, key string) string {

  val := reflect.ValueOf(flight).Elem()

  for i := 0; i < val.NumField(); i++ {
    valueField := val.Field(i)
    typeField := val.Type().Field(i)
    if key == strings.ToLower(typeField.Name) {
      return valueField.Interface().(string)
    }
  }
  return ""
}

Now while calling search function my call should be like this! result := Search(&flights, key)



标签: reflection go