是否有 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
:接口与只有方法具有无限类型集,并且具有类型术语的接口不能在任何地方使用,除非作为约束。
在下面的代码中,我定义了一个通用链表。 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 andT
supports the operations==
and!=
T
is an interface type and each type inT
's type set implementscomparable
这是一个重要的陷阱,因为基本接口类型通常支持 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
:接口与只有方法具有无限类型集,并且具有类型术语的接口不能在任何地方使用,除非作为约束。