GO GENRICS:地图密钥的类型约束?

发布于 2025-02-07 07:50:38 字数 690 浏览 0 评论 0原文

在下面的代码中,我定义了一个通用链接列表。 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

掌心的温暖 2025-02-14 07:50:38

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

预定接口类型可比较表示所有的集合
可比性的非接口类型。具体而言,T型T
如果:

实现可比

  • t不是接口类型,t支持操作==和!= 2
  • t是界面类型,t的每种类型的类型set interments bentement 可比性

即使可以比较非类型参数的接口(可能引起运行时恐慌),它们也无法实现可比性。

这是一个重要的陷阱,因为基本接口类型通常确实支持

因此,您的接口列表[x]可以直接用作映射密钥,如map [list [int]] string {}中,但它不能实现<代码>可比较,因为它具有无限类型集(它没有术语,因此任何类型都会实现它)。 cons也不能实现它,因为它具有类型list [x]的字段。对此没有“较弱”的限制。

考虑嵌入可比的约束也对地图键有效,因此,如果您确实需要函数主体中的方法islist(),则可以定义这样的约束,并让您的列表-ARE-MAP-KEY结构实现,而不是声明接口字段:

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

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

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 != 2
  • T is an interface type and each type in T's type set implements comparable

Even though interfaces that are not type parameters can be compared (possibly causing a run-time panic) they do not implement comparable.

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 in map[List[int]]string{}, but it does not implement comparable because it has an infinite type set (it has no terms, so any type implements it). And Cons doesn’t implement it either because it has a field of type List[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 method isList() 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:

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

1: the quote from the specs hints there are interface types that implement comparable, but it's effectively not possible to instantiate comparable 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 ==, like type S struct { data any }, but these types still can't instantiate comparable https://go.dev/play/p/N-pmE0XC-hB. This is a bug in the spec.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文