Because not all types are comparable, e.g. a slice. So we can't do this
var v ArbitraryType
v == reflect.Zero(reflect.TypeOf(v)).Interface()
Because not all types are comparable, e.g. a slice. So we can't do this
var v ArbitraryType
v == reflect.Zero(reflect.TypeOf(v)).Interface()
Go 1.13 introduced Value.IsZero
method in reflect
package. This is how you can check for zero value using it:
if reflect.ValueOf(v).IsZero() {
// v is zero, do something
}
Apart from basic types, it also works for Chan, Func, Array, Interface, Map, Ptr, Slice, UnsafePointer, and Struct.
As Peter Noyes points out, you just need to make sure you're not comparing a type which isn't comparable. Luckily, this is very straightforward with the reflect
package:
func IsZero(v interface{}) (bool, error) {
t := reflect.TypeOf(v)
if !t.Comparable() {
return false, fmt.Errorf("type is not comparable: %v", t)
}
return v == reflect.Zero(t).Interface(), nil
}
See an example use here.
It depends of what behavior you want when v
is an interface (for other types it's the same):
reflect.ValueOf(v).IsZero()
checks whether the value boxed in the interface is zeroreflect.ValueOf(&v).Elem().IsZero()
checks whether the interface variable is zeroDemonstration (playground):
var v interface{} = ""
fmt.Println(reflect.ValueOf(v).IsZero()) // true, the empty string "" is zero
fmt.Println(reflect.ValueOf(&v).Elem().IsZero()) // false, the interface itself is not zero
The difference is due to the argument of ValueOf
being interface{}
. If you pass an interface, it will unbox the dynamic value boxed in it. The Go docs of ValueOf
remind that:
ValueOf returns a new Value initialized to the concrete value stored in the interface i
By using ValueOf(&v)
instead "the concrete value stored in the interface i" will be a pointer to v
. Then you dereference with Elem()
to get the original value and finally check IsZero()
.
Most often than not, what you want is probably reflect.ValueOf(&v).Elem().IsZero()
, though YMMV.
With generics, you can check if a variable is zero value by using the ==
operator on a var zero T
or *new(T)
. The type parameter must be comparable (comparable
constraint or type set of comparable types).
func IsZeroVar[T ~int64 | ~string](v T) bool {
var zero T
return v == zero
}
func IsZeroNew[T ~int64 | ~string](v T) bool {
return v == *new(T)
}
If the type param is not comparable, you must fall back to reflection, as shown above.
Both of the following give me reasonable results (probably because they're the same?)
reflect.ValueOf(v) == reflect.Zero(reflect.TypeOf(v)))
reflect.DeepEqual(reflect.ValueOf(v), reflect.Zero(reflect.TypeOf(v)))
e.g. various integer 0 flavours and uninitialized struct
s are "zero"
Sadly, empty strings and arrays are not. and nil
gives an exception.
You could special case these if you wanted.