如何检查泛型类型的值是否为零值?

e.n*_*lov 6 generics go

泛型提案中有一介绍如何返回类型参数的零值,但没有提到如何检查参数化类型的值是否为零值。

我能想到的唯一方法是使用reflect.ValueOf(...).IsZero(),还有其他选择吗?

bla*_*een 6

将等于运算符与*new(T)习语一起使用。通用参数的约束必须是或嵌入的,comparable以支持相等运算符,或指定可比较类型的类型集:

\n
func IsZero[T comparable](v T) bool {\n    return v == *new(T)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果你能\xe2\x80\x99t 约束Tcomparable那么你\xe2\x80\x99 就会留下反射。Zombo\xe2\x80\x99s 建议像这样寻址变量:

\n
reflect.ValueOf(&v).Elem().IsZero()\n
Run Code Online (Sandbox Code Playgroud)\n

优于简单地reflect.ValueOf(v).IsZero()因为ValueOf需要一个interface{}参数,如果v恰好是一个接口,您就会丢失该信息。我不认为使用泛型你会经常用接口实例化一个函数,但它值得了解。所以:

\n
    func IsZero[T any](v T) bool {\n        return reflect.ValueOf(&v).Elem().IsZero()\n    }\n
Run Code Online (Sandbox Code Playgroud)\n

使用类型变量T并在比较中使用该变量也是可行的。它比 更具可读性*new(T),但需要额外的var声明。操作数仍然必须是可比较的:

\n
func IsZero[T comparable](v T) bool {\n    var zero T\n    return v == zero\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果你关心反射的性能,它确实比纯粹的可比泛型稍差:

\n
go1.18rc1 test -v -bench=. -benchmem\ngoos: darwin\ngoarch: arm64\npkg: example.com\nBenchmarkGenerics-10        1000000000      0.3295 ns/op     0 B/op   0 allocs/op\nBenchmarkReflection-10      94129023        12.26 ns/op      8 B/op   1 allocs/op\n
Run Code Online (Sandbox Code Playgroud)\n

如果您知道不会使用接口实例化基于反射的函数,则可以简化代码,从而reflect.ValueOf(v).IsZero()进行微小的改进:

\n
go1.18rc1 test -v -bench=. -benchmem\ngoos: darwin\ngoarch: arm64\npkg: example.com\nBenchmarkGenerics-10          1000000000    0.3295 ns/op     0 B/op   0 allocs/op\nBenchmarkReflection-10        94129023      12.26 ns/op      8 B/op   1 allocs/op\nBenchmarkReflectionNoAddr-10  100000000     11.41 ns/op      8 B/op   0 allocs/op\n
Run Code Online (Sandbox Code Playgroud)\n