40

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?

1

4 Answers 4

78

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())
}
Sign up to request clarification or add additional context in comments.

3 Comments

PS: should be probably modified to x == nil || x == reflect.Zero(reflect.TypeOf(x)).Interface() or it will panic on nil
@ErikAigner: well, it depends on what exactly you are looking for. If you want to be able to detect the nil interface itself too, then yes, you should add that. But in the question, you said that there's a int/string/bool/etc.. stored inside the interface
Fails with map[string]string. Is there anyway to make this supports maps short of casting and checking len ?
29

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)

var p *string
v := reflect.ValueOf(p)

fmt.Printf("1.13 v.IsZero()='%v' vs. IsZero(v)='%v'\n", v.IsZero(), IsZero(v))

// Ouput:
// 1.13 v.IsZero()='true' vs. IsZero(v)='true'

However:

v = v.Elem()
fmt.Printf("1.13 v.Elem().IsZero()='%v' vs. IsZero(v.Elem())='%v'\n", v.IsZero(), IsZero(v))

// Panic:
//
// panic: reflect: call of reflect.Value.IsZero on zero Value

As explained in issue 46320:

The problem you are describing is due to the nature of interface types in Go, and to the fact that the reflect package is built on interface types.

A value of interface type always describes some other value.
When you pass a value of interface type to reflect.ValueOf, you get a reflection object describing that other value, not the value of interface type.

To work with interface values with the reflect package you generally need to use pointers.

This doesn't have anything to do with IsZero(), it's just how the reflect package works.

package main

import (
  "fmt"
  "reflect" 
)

func main() {
  var c interface{}

  fmt.Println(reflect.ValueOf(&c).Elem().Kind())
  fmt.Println(reflect.ValueOf(&c).Elem().IsZero())
} 

Basically, one needs to call IsValid() before IsZero()

3 Comments

playground example is not working
@SamirKape It is working, but the panic message, as reported in issue 46320 is counter-intuitive and confusing indeed.
Looks like issue 61372 is not happening right away.
4

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]

8 Comments

so basically I can check via reflect.ValueOf(i) == reflect.Zero(reflect.TypeOf(int(0))) replacing int with every type I want to check?
Your question as stated in the OP IMHO doesn't require any use of reflection, IIUC(?). A zero value of interface{} compares equal to nil.
Oh, I didn't realize what you meant. I guess this illustrates my question better play.golang.org/p/qFmrij9RYQ
I'm confused. That does not compare a zero value of interface{} but an interface{} typed argument carrying one of the passed values. All of those, except for untyped the nil are not zero values. What exactly do you mean by empty/uninitialized? It doesn't seem to be the same as the zero value, but what it is then?
No, that has other problems. play.golang.org/p/Rr7kjltONj. The only way I can think to do this is play.golang.org/p/nKsjDU1OzI. However, that doesn't work in the playground because of its use of unsafe.
|
1

@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))
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.