为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
侃侃尔雅 2022-11-23 15:38:45

在下面的代码中,我定义了一个通用链表。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

}


查看完整描述

1 回答

?
森林海

TA贡献1705条经验 获得超2个赞

预先声明comparable的约束是映射键的正确的包罗万象的约束,因为它是由支持==!=(用作映射键的条件)但不是接口1的类型实现的。

这在这里提到:https ://go.dev/ref/spec#Type_constraints

预先声明的接口类型可比较表示所有可比较的非接口类型的集合。具体来说,类型 T 实现可比性,如果:

  • T不是接口类型,T支持操作==!= 2

  • T是一个接口类型,每个类型在T的类型集中实现comparable

即使可以比较不是类型参数的接口(可能导致运行时恐慌),它们也不会实现可比较。

这是一个重要的陷阱,因为基本接口类型通常支持相等运算符——比较的是它们的动态类型/值。

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

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

// may use this as a constraint

type List interface {

    comparable

    isList() bool

}

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

2:这个规则其实不包括支持的非接口类型==,比如type S struct { data any },但是这些类型仍然不能实例化comparable https://go.dev/play/p/N-pmE0XC-hB。这是规范中的错误。


查看完整回答
反对 回复 2022-11-23

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信