Tasos Bitsios
Tasos Bitsios

Reputation: 2789

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

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

Answers (3)

blackgreen
blackgreen

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

Volker
Volker

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

xarantolus
xarantolus

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

Related Questions