2

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?

1
  • 1
    Just a tip: avoid using new(T) as much as possible. It's better to just create a new literal (t := test{} or t := &test{}). There are certain cases where the use of new makes sense, but those are few and far between Commented Oct 28, 2021 at 8:32

1 Answer 1

1

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

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