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

Is there a Go generic type constraint that captures the ability to use a type as a key in a map?

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

Cons[int] does not implement 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
}

预先声明的 comparable 约束是映射键的正确 catch-all 约束,因为它由支持 ==!= 的类型实现(使用条件作为映射键),但不是接口 1.

此处提到:https://go.dev/ref/spec#Type_constraints

The predeclared interface type comparable denotes the set of all non-interface types that are comparable. Specifically, a type T implements comparable if:

  • T is not an interface type and T supports the operations == and !=
  • T is an interface type and each type in T's type set implements comparable

这是一个重要的陷阱,因为基本接口类型通常支持 equality operators — 比较的是它们的动态 types/values.

因此,您的接口 List[X] 可以直接用作映射键,如 map[List[int]]string{},但它没有实现 comparable 因为它有一个无限类型集(它没有条款,所以任何类型都可以实现它)。 Cons 也没有实现它,因为它有一个 List[X] 类型的字段。对此没有“较弱”的约束。

考虑到嵌入 comparable 的约束对映射键也是有效的,所以如果你真的需要函数体中的方法 isList() ,你可以像这样定义一个约束,并让你的lists-that-are-map-key 结构实现了它,而不是声明接口字段:

// may use this as a constraint
type List interface {
    comparable
    isList() bool
}

1:规范中的引述暗示存在实现 comparable 的接口类型,但根本不可能用任何接口实例化 comparable:接口与只有方法具有无限类型集,并且具有类型术语的接口不能在任何地方使用,除非作为约束。