英文:
How to check if the value of a generic type is the zero value?
问题
泛型提案中有一个章节,介绍了如何返回类型参数的零值,但没有提及如何检查参数化类型的值是否为零值。
我能想到的唯一方法是使用reflect.ValueOf(...).IsZero()
,是否有其他替代方法?
英文:
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?
答案1
得分: 3
使用等号运算符与*new(T)
习语一起使用。泛型参数的约束必须是comparable
,以支持等号运算符,或者指定一个可比较类型的类型集合:
func IsZero[T comparable](v T) bool {
return v == *new(T)
}
如果无法将T
约束为comparable
,则只能使用反射。Zombo建议像这样处理变量:
reflect.ValueOf(&v).Elem().IsZero()
比起简单地使用reflect.ValueOf(v).IsZero()
,这种方法更好,因为ValueOf
接受一个interface{}
参数,如果v
恰好是一个接口,你将丢失该信息。我认为在泛型中很少会使用接口实例化函数,但这也值得知道。所以:
func IsZero[T any](v T) bool {
return reflect.ValueOf(&v).Elem().IsZero()
}
使用类型为T
的变量并在比较中使用它也是有效的。它比*new(T)
更易读,但需要额外的var
声明。操作数仍然必须是可比较的:
func IsZero[T comparable](v T) bool {
var zero T
return v == zero
}
如果你担心反射的性能问题,它确实比纯可比较的泛型稍差:
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
如果你知道不会使用接口实例化基于反射的函数,你可以简化代码为reflect.ValueOf(v).IsZero()
,这会略微提高性能:
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
英文:
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
答案2
得分: 2
如果类型是可比较的,那么将值与设置为该类型的零值的变量进行比较。
var zero T
isZero := v == zero
如果类型不可比较,则使用 reflect 包:
isZero := reflect.ValueOf(&v).Elem().IsZero()
英文:
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()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论