【问题标题】:Ensure strict comparability at compile time in Go 1.20?确保 Go 1.20 编译时的严格可比性?
【发布时间】:2022-12-15 04:38:42
【问题描述】:
在 Go 1.18 和 Go 1.19 中,我可以在编译时确保一个类型是严格比较,即它支持 == 和 != 运营商,并且保证运行时不要恐慌.
例如,这对于避免无意中将字段添加到可能导致不必要的恐慌的结构很有用。
我只是尝试用它实例化comparable:
// supports == and != but comparison could panic at run time
type Foo struct {
SomeField any
}
func ensureComparable[T comparable]() {
// no-op
}
var _ = ensureComparable[Foo] // doesn't compile because Foo comparison may panic
由于 comparable 约束的定义,这在 Go 1.18 和 1.19 中是可能的:
预先声明的接口类型可比较表示所有可比较的非接口类型的集合
尽管 Go 1.18 和 1.19 规范没有提到不是接口但也不能严格比较的类型,例如[2]fmt.Stringer 或 struct { foo any },gc 编译器拒绝将它们作为 comparable 的参数。
游乐场有几个例子:https://go.dev/play/p/_Ggfdnn6OzZ
在 Go 1.20 中,实例化 comparable 将与 broader notion of comparability 对齐。这使得ensureComparable[Foo]编译尽管我不想这样.
有没有办法静态地确保与 Go 1.20 的严格可比性?
【问题讨论】:
标签:
go
generics
comparable
compile-time-type-checking
【解决方案1】:
要测试 Foo 在 Go 1.20 中是严格可比的,请实例化 ensureComparable带有类型参数受Foo约束。
// unchanged
type Foo struct {
SomeField any
}
// unchanged
func ensureComparable[T comparable]() {}
// T constrained by Foo, instantiate ensureComparable with T
func ensureStrictlyComparable[T Foo]() {
_ = ensureComparable[T]() // <---- doesn't compile
}
此解决方案原为suggested by Robert Griesemer here。
那么它是怎样工作的?
Go 1.20 引入了一个区别实施一个接口和satisfying a constraint:
类型 T 满足约束条件 C 如果
-
T实现C;或者
- C可以写成
interface{ comparable; E }的形式,其中E是基本接口,T是可比较的,实现
E。
第二个要点是允许接口和具有接口的类型实例化 comparable 的例外。
所以现在在 Go 1.20 中,由于可满足性异常,Foo 类型本身可以实例化 comparable。但是类型参数T不是Foo。类型参数的可比性定义differently:
如果类型参数是严格可比较的(见下文),则类型参数是可比较的。
[...]
如果类型集中的所有类型都严格可比较,则类型参数是严格可比较的。
T 的类型集包括一个不能严格比较的类型Foo(因为它有接口字段),因此T 不满足comparable。即使 Foo 本身也是如此。
如果 Foo 的运算符 == 和 != 可能在运行时发生恐慌,这个技巧会有效地使程序无法编译。