v1ct0r
v1ct0r

Reputation: 311

How to know if a variable of arbitrary type is Zero in Golang?

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

Upvotes: 14

Views: 8511

Answers (4)

blackgreen
blackgreen

Reputation: 44665

Reflect

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 zero
  • reflect.ValueOf(&v).Elem().IsZero() checks whether the interface variable is zero

Demonstration (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.

Go 1.18 and generics

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.

Upvotes: 4

mrpandey
mrpandey

Reputation: 1019

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.

Upvotes: 14

Gordon Childs
Gordon Childs

Reputation: 36074

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 structs are "zero"

Sadly, empty strings and arrays are not. and nil gives an exception.
You could special case these if you wanted.

Upvotes: 2

joshlf
joshlf

Reputation: 23567

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.

Upvotes: 3

Related Questions