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

Issue

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 ==?

Solution

Comparison operators == and != can be used only on parametrized types that are indeed comparable.

Those are clearly defined in the Go specs: Comparison Operators. In particular:

Slice, map, and function values are not comparable.

By using the type constraint any, you are allowing literally any type, including slices, maps and functions. Since those types are not comparable in the first place, the compiler complains with the error you mention:

cannot compare val == needle (operator == not defined for T)

Starting with Go 1.18 the language will have the predeclared identifier comparable that restricts the type parameter to those that define == and !=.

So the correct way to write your function signature is

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

For clarity’s sake, note that comparable is a predeclared identifier, not a keyword. The difference being that you can redeclare it in a narrower scope and shadow the built-in one. To be clear, the following program does compile:

package main

import "fmt"

func main() {
    comparable := 1
    fmt.Println(comparable)
}

I don’t think you will often need to redeclare the identifier, but it’s 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
}

Answered By – blackgreen

Answer Checked By – Jay B. (GoLangFix Admin)

Leave a Reply

Your email address will not be published.