Reputation: 137
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?
Upvotes: 6
Views: 5577
Reputation:
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()
Upvotes: 3
Reputation: 44665
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
Upvotes: 6