在Go语言1.20中确保编译时的严格可比性?

4si2a6ki  于 2022-12-16  发布在  Go
关注(0)|答案(1)|浏览(159)

在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.Stringerstruct { foo any },但gc编译器确实拒绝将它们作为comparable的参数。
Playground与几个例子:https://go.dev/play/p/_Ggfdnn6OzZ
在Go语言1.20中,示例化comparable将与更广泛的相似性概念保持一致,这使得ensureComparable[Foo]可以编译**,尽管我不希望它**。
有没有办法静态地保证与Go 1.20的严格可比性?

41zrol4v

41zrol4v1#

为了测试Foo在Go语言1.20中是否严格可比,可以用一个受Foo约束的类型参数**示例化ensureComparable

// 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
}

这个解决方案最初是由罗伯特·格里泽默提出的。
那么它是如何工作的呢?
Go语言1.20引入了实现接口和满足约束之间的区别:
类型T满足约束C,如果

  • T实现了C;或
  • C可以写成interface{ comparable; E }的形式,其中E是一个基本接口,T是类似的,它实现了E

第二个要点是允许接口和具有接口的类型示例化comparable的例外。
所以在Go语言1.20中,由于满足性异常,类型Foo本身可以示例化comparable,但是类型参数T不是Foo,类型参数的可比性定义不同:
如果类型参数严格可比,则它们是可比的(见下文)。
[...]
如果类型参数的类型集中的所有类型都严格可比,则类型参数是严格可比的。
T的类型集包括一个不严格可比的类型Foo(因为它有一个接口字段),因此T不满足comparable,即使Foo本身满足。
如果Foo的操作符==!=在运行时可能发生异常,则此技巧会有效地使程序无法编译。

相关问题