Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get zero value of a field type

Tags:

go

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 the value of a field is different from the field's default value.

Currently, I have this (works, but a bit smelly):

...
qsMap := make(map[string]interface{})
var defaultTime time.Time
var defaultString string
...
// get the field name and value
fieldName := s.Type().Field(i).Tag.Get("bson")
fieldValue := valueField.Interface()

// use reflection to determine the TYPE of the field and apply the proper formatting
switch fieldValue.(type) {
case time.Time:
if fieldValue != defaultTime {
    qsMap[fieldName] = fieldValue
}
case string:
if fieldValue != defaultString {
    qsMap[fieldName] = fieldValue
}
...
}

Seems to me that there should be a way to avoid the type switch in this case - what I'm trying to do is build up a map of field/values that have a value different from their default zero value, something like:

// doesn't work -- i.e., if fieldValue of type string would be compared against "", etc.
if fieldValue != reflect.Zero(reflect.Type(fieldValue)) {
    qsMap[fieldName] = fieldValue
}

Is there an elegant way to accomplish this?

Thanks!

like image 538
user2644113 Avatar asked May 09 '14 01:05

user2644113


People also ask

What is the default value of zero?

If an enum does not define an item with a value of zero, its default value will be zero.

What is the zero value of a struct in Golang?

A zero value struct is simply a struct variable where each key's value is set to their respective zero value. This article is part of the Structs in Go series. We can create a zero value struct using the var statement to initialize our struct variable.

What is the default value of type bool in Go?

The default value for the bool type in the Go programming language is false.


1 Answers

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()
}
like image 179
James Henstridge Avatar answered Sep 24 '22 06:09

James Henstridge