Reputation: 2789
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 ==
?
Upvotes: 19
Views: 15900
Reputation: 44647
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
Note 1: types constrained by comparable
allow ==
and !=
but not order operators <
, >
, <=
and >=
. For details, see In Go generics, why can't I use comparable constraint with order operators?
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.
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
}
Upvotes: 26
Reputation: 42413
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.
Upvotes: 3
Reputation: 2029
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.
Upvotes: 9