是否有 Go 泛型类型约束捕获将类型用作映射中的键的能力?

在下面的代码中,我定义了一个通用链表。Go1.18 很乐意使用列表的实例作为映射的键。但是,最后一行未注释时不会编译;我收到错误:


Cons[int] 没有实现 comparable


是否有我可以使用的较弱类型约束来挑选出可用作键的类型,或者这是有意为之,还是编译器错误?


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

}


侃侃尔雅
浏览 69回答 1
1回答

森林海

预先声明comparable的约束是映射键的正确的包罗万象的约束,因为它是由支持==和!=(用作映射键的条件)但不是接口1的类型实现的。这在这里提到:https ://go.dev/ref/spec#Type_constraints预先声明的接口类型可比较表示所有可比较的非接口类型的集合。具体来说,类型 T 实现可比性,如果:T不是接口类型,T支持操作==和!= 2T是一个接口类型,每个类型在T的类型集中实现comparable即使可以比较不是类型参数的接口(可能导致运行时恐慌),它们也不会实现可比较。这是一个重要的陷阱,因为基本接口类型通常支持相等运算符——比较的是它们的动态类型/值。因此,您的接口List[X]可以直接用作映射键,如中所示map[List[int]]string{},但它不会实现comparable,因为它具有无限类型集(它没有术语,因此任何类型都可以实现它)。也Cons没有实现它,因为它有一个 type 字段List[X]。对此没有“较弱”的约束。考虑到嵌入comparable的约束对映射键也有效,所以如果你真的需要isList()函数体中的方法,你可以定义一个这样的约束,并让你的列表是映射键结构实现它,而不是声明一个接口字段:// may use this as a constrainttype List interface {    comparable    isList() bool}1:规范中的引述暗示存在实现的接口类型comparable,但实际上根本不可能comparable用任何接口实例化:只有方法的接口具有无限类型集,并且具有类型术语的接口不能在任何地方使用,除了作为约束。2:这个规则其实不包括支持的非接口类型==,比如type S struct { data any },但是这些类型仍然不能实例化comparable https://go.dev/play/p/N-pmE0XC-hB。这是规范中的错误。
打开App,查看更多内容
随时随地看视频慕课网APP