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
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{}}
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With