在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
的参数。
Playground与几个例子:https://go.dev/play/p/_Ggfdnn6OzZ
在Go语言1.20中,示例化comparable
将与更广泛的相似性概念保持一致,这使得ensureComparable[Foo]
可以编译**,尽管我不希望它**。
有没有办法静态地保证与Go 1.20的严格可比性?
1条答案
按热度按时间41zrol4v1#
为了测试
Foo
在Go语言1.20中是否严格可比,可以用一个受Foo
约束的类型参数**示例化ensureComparable
。这个解决方案最初是由罗伯特·格里泽默提出的。
那么它是如何工作的呢?
Go语言1.20引入了实现接口和满足约束之间的区别:
类型
T
满足约束C
,如果T
实现了C
;或interface{ comparable; E }
的形式,其中E
是一个基本接口,T
是类似的,它实现了E
。第二个要点是允许接口和具有接口的类型示例化
comparable
的例外。所以在Go语言1.20中,由于满足性异常,类型
Foo
本身可以示例化comparable
,但是类型参数T
不是Foo
,类型参数的可比性定义不同:如果类型参数严格可比,则它们是可比的(见下文)。
[...]
如果类型参数的类型集中的所有类型都严格可比,则类型参数是严格可比的。
T
的类型集包括一个不严格可比的类型Foo
(因为它有一个接口字段),因此T
不满足comparable
,即使Foo
本身满足。如果
Foo
的操作符==
和!=
在运行时可能发生异常,则此技巧会有效地使程序无法编译。