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.
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.
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.
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.
© 2022 - 2024 — McMap. All rights reserved.