Reputation:
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
nil
How do I check this?
Upvotes: 39
Views: 35331
Reputation: 1324148
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 toreflect.ValueOf
, you get a reflection object describing that other value, not the value of interface type.To work with
interface
values with thereflect
package you generally need to use pointers.This doesn't have anything to do with
IsZero()
, it's just how thereflect
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()) }
Upvotes: 27
Reputation: 912
@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))
}
Upvotes: 1
Reputation: 122439
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:
nil
interface value, which is an interface value that doesn't have an underlying value. This is the zero value of an interface type.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())
}
Upvotes: 78
Reputation: 91243
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]
Upvotes: 4