在GO 1.18和GO 1.19中,我可以在编译时确保类型为strictly comparable,即它支持==!=运算符,而这些运算符保证为not panic at run time.

例如,这有助于避免无意中将字段添加到可能导致不必要的死机的 struct 中.

我只是try 用它实例化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的参数.

有几个例子的操场:https://go.dev/play/p/_Ggfdnn6OzZ

在Go 1.20中,实例化comparable将与broader notion of comparability对齐.这使得ensureComparable[Foo]编译even though I don't want it to.

有没有办法静态地确保与GO 1.20的严格可比性?

推荐答案

要测试Foo在GO 1.20中是否具有严格的可比性,请实例化Foo约束的ensureComparable with a type parameter.

// 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引入了implementingsatisfying 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的运算符==!=可能在运行时死机,这个技巧实际上会使程序无法编译.

Go相关问答推荐

为什么使用append时Go切片的初始容量会随着int32和int64类型的不同而变化?

如何使用Docker Compose配置Go,使main. go文件位于/CMD文件夹中

理解Golang并发:缓冲通道的意外行为

如何防止程序B存档/删除围棋中程序A当前打开的文件?

Go SQLCMD比Windows本机版本慢吗?

如何在gofiber/websocket/v2中设置状态代码和原因

如何为ANTLR4目标Go调试监听器

GORM中是否可能自动迁移具有循环关系的表?

Go time.Parse 无效的 ISO 日期

闭包所处的环境范围是什么?

go-echo中如何防止+转义

如何在 GORM 中迭代一个 int 数组

使用 oklog/run 来自 Go 编译器的错误(无值)用作值

Golang SSH客户端错误无法验证,try 的方法[无公钥],没有支持的方法

HCL 解码:具有多个标签的块

Go:如何创建一个可以提供配置文件中描述的 url 的服务器

防止在 Go 公用文件夹中列出目录

处理程序中的无限循环

手动下载并放置一个 golang mod 文件

为什么在 unsafe.Sizeof() 中取消引用 nil 指针不会导致panic ?