使用泛型:类型参数T不能使用==进行比较。

huangapple go评论75阅读模式
英文:

Go with Generics: type parameter T is not comparable with ==

问题

我正在玩弄Go语言中的泛型,尝试编写一些通用的数组函数。

我遇到的错误是:

invalid operation: val == needle (type parameter T is not comparable with ==)

我可以通过使用反射来解决这个问题:

if reflect.ValueOf(val).Interface() == reflect.ValueOf(needle).Interface()

然而,是否有一个(内部的?)接口用于“可比较”类型,其中定义了==运算符,我可以使用它来替代any

是否真的有不支持使用==进行比较的类型?

英文:

I am playing around with Go Generics in the playground, trying to write some generic array functions.

https://gotipplay.golang.org/p/vS7f_Vxxy2j

package main

import (
	"fmt"
)

func array_has[T any](haystack []T, needle T) bool {
	for _, val := range haystack {
		if val == needle {
			return true
		}
	}
	return false
}


func main() {
	arr := []string{"A","B","C"}
	fmt.Println(array_has(arr, "T"))
}

The error I am getting is:

> invalid operation: val == needle (type parameter T is not comparable with ==)

I can work around it by using reflect:

if reflect.ValueOf(val).Interface() == reflect.ValueOf(needle).Interface()

Go2 Playground: https://gotipplay.golang.org/p/9ZVZafQ_9JK

However, is there an (internal?) interface for "comparable" types, for which == is defined, that I can use instead of any?

Are there really even types that do not support comparison with ==?

答案1

得分: 19

比较运算符==!=只能用于确实可比较的参数化类型。参考Go规范:比较运算符

> 切片、映射和函数值是不可比较的。

通过使用类型约束any,你可以允许任何类型,包括那些不可比较的类型(切片、映射和函数),因此会产生你看到的编译器错误。

相反,你可以使用预声明的约束comparable,将类型参数限制为定义了==!=的类型,除了接口

因此,编写函数签名的正确方式是:

func array_has[T comparable](haystack []T, needle T) bool

除了comparable,你还可以使用嵌入comparable的接口约束,或者更严格的约束,只包括可比较的类型,例如:

func array_has[T ~string](haystack []T, needle T) bool

<hr>

注意1:受comparable约束的类型允许使用==!=,但不允许使用顺序运算符&lt;&gt;&lt;=&gt;=。详情请参考https://stackoverflow.com/questions/70562572/in-go-generics-why-cant-i-use-comparable-constraint-with-order-operators/70562597#70562597

<hr>

注意2:comparable是一个预声明的标识符,而不是关键字。区别在于你可以在较窄的作用域中重新声明它并遮蔽内置的标识符。为了明确起见,这个程序可以编译。这是一个需要了解的好消息,因为这是泛型实现与早期Go版本向后兼容的原因之一,即具有变量名为"comparable"的旧程序仍然可以编译。

<hr>

约束comparable也是你在映射键上声明类型参数所需的。映射键还必须支持==!=运算符。

func useKeys[K comparable, V any](m map[K]V) {
    // 使用映射键
}
英文:

Comparison operators == and != can be used only on parametrized types that are indeed comparable. Referring to the Go specs: Comparison Operators:

> Slice, map, and function values are not comparable.

By using the type constraint any, you are allowing literally any type, including those that are not comparable (slices, maps and functions), thus producing the compiler error you saw.

Instead you can use the predeclared constraint comparable that restricts the type parameter to those that define == and !=, except interfaces.

So the correct way to write your function signature is

func array_has[T comparable](haystack []T, needle T) bool

Instead of comparable you can also use interface constraints that embed comparable, or stricter constraints that include only types that are comparable, e.g.:

func array_has[T ~string](haystack []T, needle T) bool

<hr>

Note 1: types constrained by comparable allow == and != but not order operators &lt;, &gt;, &lt;= and &gt;=. For details, see https://stackoverflow.com/questions/70562572/in-go-generics-why-cant-i-use-comparable-constraint-with-order-operators/70562597#70562597

<hr>

Note 2: comparable is a predeclared identifier, not a keyword. The difference is that you can redeclare it in a narrower scope and shadow the built-in one. To be clear this program compiles. This is good to know, as it's one of the reasons why the generics implementation is backwards compatible with earlier Go versions, i.e. old programs with variables named "comparable" will still compile.

<hr>

The constraint comparable is also what you need to declare type parameters on map keys. Map keys must also support == and != operators.

func useKeys[K comparable, V any](m map[K]V) {
    // use map keys
}

答案2

得分: 8

如您所见,根据提案,"适用于任何类型的操作",直接比较any类型的值是不可能的。然而,这并不是唯一可能的泛型约束。

这就是comparable的用途,它允许使用比较操作。您可以将any替换为它,您的函数将正常工作:

func array_has[T comparable](haystack []T, needle T) bool {
    for _, val := range haystack {
        if val == needle {
            return true
        }
    }
    return false
}

这在playground上可以运行。

我不确定是否存在无法使用==进行比较的类型,所以这似乎有点奇怪。

英文:

As you can see from the proposal, section "Operations permitted for any type", it is not directly possible to compare values of the any type. However, it's not the only possible generic constraint.

This is where comparable comes in, it allows using comparisons. You can replace the any with it and your function will work:

func array_has[T comparable](haystack []T, needle T) bool {
	for _, val := range haystack {
		if val == needle {
			return true
		}
	}
	return false
}

This works on the playground.

I'm not sure if there are types that are not comparable using ==, so that seems a bit weird.

答案3

得分: 3

是的,有很多。其中一个显著的例子是映射和切片,还有函数等。

如果有疑问,请参考规范。

英文:

> Are there really even types that do not support comparison with == ?

Yes, a lot. Maps and slices being a prominent example, functions an other.

When in doubt: Consult the spec.

huangapple
  • 本文由 发表于 2021年6月20日 16:19:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/68053957.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定