I have a struct containing many fields - I\'ve figured out how to extract the field name, value, and tag information using reflection. What I also want to do is to determine if
For types that support the equality operation, you can just compare interface{}
variables holding the zero value and field value. Something like this:
v.Interface() == reflect.Zero(v.Type()).Interface()
For functions, maps and slices though, this comparison will fail, so we still need to include some special casing. Further more, while arrays and structs are comparable, the comparison will fail if they contain non-comparable types. So you probably need something like:
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Func, reflect.Map, reflect.Slice:
return v.IsNil()
case reflect.Array:
z := true
for i := 0; i < v.Len(); i++ {
z = z && isZero(v.Index(i))
}
return z
case reflect.Struct:
z := true
for i := 0; i < v.NumField(); i++ {
z = z && isZero(v.Field(i))
}
return z
}
// Compare other types directly:
z := reflect.Zero(v.Type())
return v.Interface() == z.Interface()
}
You can switch on the Kind()
of the Value
and use the appropriate accessor (many fewer kinds than types). Something like:
switch valueField.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if valueField.Int() == 0 {...}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if valueField.Uint() == 0 {...}
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
if valueField.IsNil() {...}
//add more cases for Float, Bool, String, etc (and anything else listed http://golang.org/pkg/reflect/#Kind )
}
You could also get a zeroed instance of a value by using reflect.Zero(valueField.Type())
but it is not safe to compare that with valueField since some types (such as slices and maps) do not support equality and would panic.
I couldn't post a comment, but the accepted answer panics if you provide a struct with any unexported fields. The trick I've found is to check if the field can be set - essentially ignoring any unexported fields.
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Func, reflect.Map, reflect.Slice:
return v.IsNil()
case reflect.Array:
z := true
for i := 0; i < v.Len(); i++ {
z = z && isZero(v.Index(i))
}
return z
case reflect.Struct:
z := true
for i := 0; i < v.NumField(); i++ {
if v.Field(i).CanSet() {
z = z && isZero(v.Field(i))
}
}
return z
case reflect.Ptr:
return isZero(reflect.Indirect(v))
}
// Compare other types directly:
z := reflect.Zero(v.Type())
result := v.Interface() == z.Interface()
return result
}