'How to check if the value of a generic type is the zero value?
The generics proposal has a section on how to return the zero value of a type parameter, but it does not mention how to check if a value of a parametrized type is the zero value.
The only way I can think of is to use reflect.ValueOf(...).IsZero(), is there an alternative?
Solution 1:[1]
Use the equal operator with the *new(T) idiom. The constraint of the generic param must be, or embed, comparable to support the equality operator, or specify a type set of comparable types:
func IsZero[T comparable](v T) bool {
return v == *new(T)
}
If you can’t constrain T to comparable then you’re left with reflection. Zombo’s suggestion to address the variable like this:
reflect.ValueOf(&v).Elem().IsZero()
is superior to simply reflect.ValueOf(v).IsZero() because ValueOf takes an interface{} argument and if v just happens to be an interface you would loose that information. I don’t think that with generics you will often instantiate a function with an interface but it’s worth knowing. So:
func IsZero[T any](v T) bool {
return reflect.ValueOf(&v).Elem().IsZero()
}
Using a variable of type T and using that in the comparison also works. It is more readable than *new(T), but it requires an additional var declaration. The operands still must be comparable:
func IsZero[T comparable](v T) bool {
var zero T
return v == zero
}
If you are concerned about the performance of reflection, it is indeed slightly worse than pure comparable generics:
go1.18rc1 test -v -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: example.com
BenchmarkGenerics-10 1000000000 0.3295 ns/op 0 B/op 0 allocs/op
BenchmarkReflection-10 94129023 12.26 ns/op 8 B/op 1 allocs/op
If you know you won't instantiate the reflect-based function with an interface, you can simplify the code to reflect.ValueOf(v).IsZero() which gives a minor improvement:
go1.18rc1 test -v -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: example.com
BenchmarkGenerics-10 1000000000 0.3295 ns/op 0 B/op 0 allocs/op
BenchmarkReflection-10 94129023 12.26 ns/op 8 B/op 1 allocs/op
BenchmarkReflectionNoAddr-10 100000000 11.41 ns/op 8 B/op 0 allocs/op
Solution 2:[2]
If the type is comparable, then compare the value to a variable set to the zero value of the type.
var zero T
isZero := v == zero
Use the reflect package if the type is not comparable:
isZero := reflect.ValueOf(&v).Elem().IsZero()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
