I have a piece of reflection code that attempts to get the field on a struct by name and then checks if the field exists:
type test struct {
A bool
B bool
}
t := new(test)
metaValue := reflect.ValueOf(t).Elem()
field := metaValue.FieldByName(name)
if field.IsZero() {
glog.Errorf("Field %s was not on the struct", inner)
}
According to the documentation on FieldByName
, this function should return a zero value if no field was found. However, the very next line panics with the error:
panic: reflect: call of reflect.Value.IsZero on zero Value
goroutine 268 [running]:
reflect.Value.IsZero({0x0, 0x0, 0x112a974})
reflect/value.go:1475 0x27f
According to this GitHub issue, this should only happen if the Value contains nil (i.e. no type) and IsValid
should be used instead. Why is this happening?
CodePudding user response:
Value.IsZero()
reports whether the wrapped value is the zero value of its type. This is not the same as the reflect.Value
itself being zero (zero value of reflect.Value
which is a struct).
Also note that t
in your code is not a struct value, its a pointer to struct. Use Value.Elem()
to nagivate to the wrapped struct value (or don't start from a pointer).
If the field does not exist, Value.FieldByName()
returns the zero value of reflect.Value
, not a non-zero reflect.Value
holding the zero value of some type; there is no type info if a field is not found.
So to check if the field does not exist, check if the reflect.Value
itself is zero, by comparing it to reflect.Value{}
:
if field == (reflect.Value{}) {
log.Printf("Field %s was not on the struct", name)
}
Testing it:
type test struct {
A bool
B bool
x bool
}
v := new(test)
metaValue := reflect.ValueOf(v).Elem()
for _, name := range []string{"A", "x", "y"} {
field := metaValue.FieldByName(name)
if field == (reflect.Value{}) {
log.Printf("Field %s was not on the struct", name)
}
}
This will output (try it on the Go Playground):
2009/11/10 23:00:00 Field y was not on the struct