golang/go

reflect: reflect.Value.IsZero

lukescott opened this issue ยท 17 comments

Many encoding packages have a custom internal "isEmptyValue" function. The
functionality is pretty much identical. This expands to other packages that use reflect,
such as an ORM (is primary key set?).

It would be useful to have a standard "IsZero" method on reflect.Value,
something like this (copied from encoding/xml):

func (v Value) IsZero() bool {
    switch v.Kind() {
    case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
        return v.Len() == 0
    case reflect.Bool:
        return !v.Bool()
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return v.Int() == 0
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
        return v.Uint() == 0
    case reflect.Float32, reflect.Float64:
        return v.Float() == 0
    case reflect.Interface, reflect.Ptr:
        return v.IsNil()
    }
    return false
}

Comment 1:

At least for Map and Slice, though, there's a difference between nil and Len 0.

Comment 2:

I believe this is correct:
func (v Value) IsZero() bool {
    switch v.Kind() {
    case reflect.Array, reflect.String:
        return v.Len() == 0
    case reflect.Bool:
        return !v.Bool()
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return v.Int() == 0
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
        return v.Uint() == 0
    case reflect.Float32, reflect.Float64:
        return v.Float() == 0
    case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
        return v.IsNil()
    }
    return false
}
it seems a reasonable feature request.

Labels changed: added release-go1.4, repo-main.

Status changed to Accepted.

Comment 3:

r's suggested revision still slightly deviates from the language defined notion of a
type's zero value. Arrays and structs are zero if all their members have respective zero
values. Yes, zero-size types (such as with the above array length check) are guaranteed
to be their own zero value, but that's not a particularly common scenario.
Zero length slices and maps would only be zero if they're also nil.
rsc commented

Comment 4:

We could define IsZero but it won't be usable in any of the packages that define their
own 'emptiness' predicates.

Comment 5:

Perhaps there could be an "IsZero" and "IsEmpty" method. "IsEmpty" would be the same as
"IsZero", but also include zero length slices and maps.
Including structs would be nice, although the current omitempty implementations in
encoding/* don't. However, that doesn't mean those implementations have to use "IsEmpty"
for Go 1. Third party / internal encoding libraries may choose to use the method though,
which still makes it useful to implement.
rsc commented

Comment 6:

Maybe for 1.5.

Labels changed: added release-go1.5, removed release-go1.4.

rsc commented

It's not clear to me what would use IsZero if we added it.
It's easy for packages to define what they really want themselves.
I'm wary of continuing to add to reflect.
I don't want it to be a kitchen sink of helpers.

Not arguing for or against, just a datapoint on intuitiveness: I found this page while looking for a reflect.IsZero that would actually test for zero values of structs.

reflect.IsZero(x) feels like it should return true for any y in var x y, to me.

But this is moot if it's not added.

rsc commented

Postponing to Go 1.7 (Go1.6Maybe was a misclick), along with discussion of omitzero in xml and json.

CL https://golang.org/cl/23064 mentions this issue.

equt commented

Any updates on this topic? This should be a reasonable feature request.

I'll turn this into a proposal for consideration.

Note that thereโ€™s also discussion of an internal runtime.isZero for optimized codegen:
#23929

Proposal tentatively approved. @rsc any objections?

Change https://golang.org/cl/171337 mentions this issue: reflect: implement Value.IsZero()

So, this will be available in Go 1.13?