通过Go中的反射检测空值的快速方法

I have a int/string/bool/etc.. value stored in an interface{} and want to determine if it's uninitialized, meaning that it has a value of either

  • 0
  • ""
  • false
  • or nil

How do I check this?

From what I understand, you want something like:

func IsZeroOfUnderlyingType(x interface{}) bool {
    return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}

When talking about interfaces and nil, people always get confused with two very different and unrelated things:

  1. A nil interface value, which is an interface value that doesn't have an underlying value. This is the zero value of an interface type.
  2. A non-nil interface value (i.e. it has an underlying value), but its underlying value is the zero value of its underlying type. e.g. the underlying value is a nil map, nil pointer, or 0 number, etc.

It is my understanding that you are asking about the second thing.


Update: Due to the above code using ==, it won't work for types that are not comparable. I believe that using reflect.DeepEqual() instead will make it work for all types:

func IsZeroOfUnderlyingType(x interface{}) bool {
    return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

The zero value* of type interface{} is only nil, not 0 or "" or false.

package main

import "fmt"

func main() {
        var v interface{}
        fmt.Println(v == nil, v == 0, v == "", v == false)
}

(Also http://play.golang.org/p/z1KbX1fOgB)


Output

true false false false

*: [Q]When memory is allocated to store a value, either through a declaration or a call of make or new, and no explicit initialization is provided, the memory is given a default initialization. Each element of such a value is set to the zero value for its type: false for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps.[/Q]

@newacct's answer can't detect raw zero value, calling reflect.Value.Interface() on which will cause error. It can use reflect.Value.IsValid() to check that.

// IsValid reports whether v represents a value.
// It returns false if v is the zero Value.
// If IsValid returns false, all other methods except String panic.
// Most functions and methods never return an invalid value.
// If one does, its documentation states the conditions explicitly.
func (v Value) IsValid() bool 

Update the methods:

func IsZero(v reflect.Value) bool {
    return !v.IsValid() || reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}

func TestIsZero(t *testing.T) {
    var p *string
    v := reflect.ValueOf(p)

    assert.Equal(t, true, v.IsValid())
    assert.True(t, IsZero(v))

    assert.Equal(t, uintptr(0), v.Pointer())

    v = v.Elem()
    assert.Equal(t, false, v.IsValid())
    assert.True(t, IsZero(v))
}

Go 1.13 (Q3 2019) should simplify that detection process:

The new Value.IsZero() method reports whether a Value is the zero value for its type.

It is implemented in src/reflect/value.go, from commit c40bffd and CL 171337, resolving issue 7501

See playground example (as soon as Go 1.13 is supported)