37

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?

  • For more up to date and straightforward answers, see also [How to know if a variable of arbitrary type is Zero in Golang?](https://stackoverflow.com/q/33115946) – blackgreen Feb 21 '22 at 10:05

4 Answers4

77

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())
}
newacct
  • 119,665
  • 29
  • 163
  • 224
  • 6
    PS: should be probably modified to `x == nil || x == reflect.Zero(reflect.TypeOf(x)).Interface()` or it will panic on `nil` –  Dec 18 '12 at 13:13
  • 1
    @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 – newacct Dec 18 '12 at 18:54
  • 1
    Fails with `map[string]string`. Is there anyway to make this supports maps short of casting and checking `len` ? – chakrit Sep 06 '16 at 07:35
23

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()

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • playground example is not working – Samir Kape Jul 28 '22 at 11:39
  • 1
    @SamirKape It is working, but the panic message, as reported in [issue 46320](https://github.com/golang/go/issues/46320#issuecomment-846403868) is counter-intuitive and confusing indeed. – VonC Jul 28 '22 at 12:12
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]

zzzz
  • 87,403
  • 16
  • 175
  • 139
  • so basically I can check via `reflect.ValueOf(i) == reflect.Zero(reflect.TypeOf(int(0)))` replacing `int` with every type I want to check? –  Dec 16 '12 at 15:09
  • 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`. – zzzz Dec 16 '12 at 15:12
  • Oh, I didn't realize what you meant. I guess this illustrates my question better http://play.golang.org/p/qFmrij9RYQ –  Dec 16 '12 at 15:23
  • 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? – zzzz Dec 16 '12 at 15:29
  • No, that has other problems. http://play.golang.org/p/Rr7kjltONj. The only way I can think to do this is http://play.golang.org/p/nKsjDU1OzI. However, that doesn't work in the playground because of its use of unsafe. – Stephen Weinberg Dec 16 '12 at 15:38
  • I quite don't get what's so hard to understand about my question. I just wanted to know how to check if an `int`/`string`/`pointer`/etc.. stored in an `interface{}` is `0`/`""`/`pointer`/etc... –  Dec 16 '12 at 16:21
  • The confusion stems from that such value is not "empty/uninitialized" as that was asked in the question (before the recent edit). Even after that edit the new use of the word "uninitialized" is not making too much sense. There are only zero values and non zero values of every Go entity. You probably mean 'uninitialized' wrt semantics of some specific goal, but no one knows that. Precise questions, using the by-specs defined terms, get better answers ;-) – zzzz Dec 16 '12 at 16:28
  • Sorry about that, but you can't assume people asking stupid questions (like me) have a complete grasp of Go's terminology :) –  Dec 17 '12 at 04:40
0

@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))
}
Geln Yang
  • 902
  • 2
  • 20
  • 36