GO GENRICS:地图密钥的类型约束?
在下面的代码中,我定义了一个通用链接列表。 GO1.18很高兴将列表实例用作地图的键。但是,最后一行,如果没有注销,则不会编译。我得到错误:
cons [int]不实施可比性
较弱的类型约束,我可以使用可以挑选出可以用作密钥的类型,还是是打算的,还是编译器错误?
package main
import "fmt"
type List[X any] interface {
isList()
}
type Cons[X any] struct {
Data X
Next List[X]
}
func (Cons[X]) isList() {}
type Nil[X any] struct{}
func (Nil[X]) isList() {}
func id[X comparable](x X) X { return x }
func main() {
x := Cons[int]{5, Nil[int]{}}
m := map[List[int]]string{}
m[x] = "Hi" // succeeds
fmt.Println(m[x]) // prints "Hi"
// fmt.Println(id(x)) // fails
}
In the code below, I define a generic linked list. Go1.18 is happy to use an instance of the list as a key to a map. However, the last line, when uncommented, doesn't compile; I get the error:
Cons[int] does not implement comparable
Is there a weaker type constraint I can use that picks out those types that can be used as keys, or is this intended, or is it a compiler bug?
package main
import "fmt"
type List[X any] interface {
isList()
}
type Cons[X any] struct {
Data X
Next List[X]
}
func (Cons[X]) isList() {}
type Nil[X any] struct{}
func (Nil[X]) isList() {}
func id[X comparable](x X) X { return x }
func main() {
x := Cons[int]{5, Nil[int]{}}
m := map[List[int]]string{}
m[x] = "Hi" // succeeds
fmt.Println(m[x]) // prints "Hi"
// fmt.Println(id(x)) // fails
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
GO 1.20(2023年2月)
可比性
是地图键的正确捕获约束。所有类型与GO Spec 可比,可以满足
可比
约束。您的代码将按照1.20的预期进行编译。最终,这修复了以前的GO版本中有关规格可相比之下的类型vs
可比性
类型的不一致性。有关详细信息,请参见下文。GO 1.18和1.19
predeclared
可比性
约束是地图键的正确约束,但是它只能通过严格可比较类型来实例化,即支持= ==的类型
和!=
(用作地图密钥的条件),但不会在运行时感到恐慌。此不包括界面 1 。这里提到的这是: https://go.dev/ref/sepec#type_constraints
这是一个重要的陷阱,因为基本接口类型通常确实支持
因此,您的接口
列表[x]
可以直接用作映射密钥,如map [list [int]] string {}
中,但它不能实现<代码>可比较,因为它具有无限类型集(它没有术语,因此任何类型都会实现它)。cons
也不能实现它,因为它具有类型list [x]
的字段。对此没有“较弱”的限制。考虑嵌入
可比
的约束也对地图键有效,因此,如果您确实需要函数主体中的方法islist()
,则可以定义这样的约束,并让您的列表-ARE-MAP-KEY结构实现,而不是声明接口字段:1:从规格提示的报价提示中,有界面类型可以实现
可比性
,而是实际上,根本不可能使用任何接口实例化可比性:仅具有方法的接口具有无限类型集,并且具有类型项的接口不能在任何地方使用,除了作为约束。< < sup> 2:此规则实际上不涵盖支持
==
的非接口类型,例如type s struct {any}
,但是这些类型仍然无法实例可比/n-pme0xc-hb 。这是规格中的错误。
Go 1.20 (February 2023)
comparable
is the correct catch-all constraint for map keys.All types that are comparable as per the Go spec, even if the comparison may panic at run time, can satisfy the
comparable
constraint. Your code will compile as expected in 1.20.This finally fixes the inconsistency in previous Go version about spec-comparable types vs
comparable
types. See below for details.Go 1.18 and 1.19
The predeclared
comparable
constraint is the correct constraint for map keys, however it can be instantiated only by strictly comparable types, i.e. types that support==
and!=
(condition for being used as map keys) but won't panic at run time. This excludes interfaces1.This is mentioned here: https://go.dev/ref/spec#Type_constraints
This is an important gotcha, because basic interface types normally do support the equality operators — what is compared is their dynamic types/values.
Therefore, your interface
List[X]
can be used as a map key directly, as inmap[List[int]]string{}
, but it does not implementcomparable
because it has an infinite type set (it has no terms, so any type implements it). AndCons
doesn’t implement it either because it has a field of typeList[X]
. There is no "weaker" constraint for this.Consider that constraints that embed
comparable
are also valid for map keys, so if you really need the methodisList()
in the function body, you can define a constraint like this, and have your lists-that-are-map-key structs implement that, instead of declaring an interface field:1: the quote from the specs hints there are interface types that implement
comparable
, but it's effectively not possible to instantiatecomparable
with any interface at all: interfaces with only methods have an infinite type set, and interfaces with type terms can't be used anywhere except as constraints.2: this rule actually doesn't cover non-interface types that support
==
, liketype S struct { data any }
, but these types still can't instantiatecomparable
https://go.dev/play/p/N-pmE0XC-hB. This is a bug in the spec.