Convert a type (int, float etc) to `T` [go1.18]

Issue

Scenario:
Go 1.18 that support generics

Problem:
Unable to convert a number into generics.

Explanation:
I’m trying to port my general purpose library to support generics. I’m dealing with number cast error.

I’ve defined a package that contains all the number types as following:

package types

type Number interface {
    int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64
}

I’ve no particular problem to deal with generics. The only thing that I’ve not understood is:
How to convert a defined type (like int) to a generic?

Let’s assume the following example:

// FindIndexValue is delegated to retrieve the index of the given value into the input array.
func FindIndexValue[T types.Number](array []T, value T) []T {
    var indexs []T
    for i := range array {
        if array[i] == value {
            indexs = append(indexs, i)
        }
    }
    return indexs
}

In the above snippet, the error is located in the line:

 ...
 for i := range array {
   ...
}

This because the range builtin iterate the array and return the index (int) of the given position.

The question is:
How can I convert the defined type (int in this case) to the generic T?
Error:
cannot use i (variable of type int) as type T in argument to append

Solution

You convert a value to a parameter type as you would normally do.

From the proposal Type conversions:

In a function with two type parameters From and To, a value of type From may be converted to a value of type To if all the types in the type set of From’s constraint can be converted to all the types in the type set of To’s constraint.

In this case you don’t really have a From because it is the slice index int; the destination type would be T (even though this is probably not what you want, see below why). Of course int can be converted to T‘s type set due to it including only numerical types (albeit with truncation or loss of precision in case of int8 or float64!)

indexs = append(indexs, T(i))

however your program is declaring the slice of indices as []T, which means that instantiating your generic function as:

is := FindIndexValue([]float64{1,5,7,9}, float64(9))

will yield a result of type []float64. Since the returned values are slice indices, which are always int, this doesn’t make a lot of sense.

Better would be to return simply []int:

func FindIndexValue[T Number](array []T, value T) []int {
    var indices []int
    for i := range array {
        // match on the generically typed slice item
        if array[i] == value {
            // store slice indices as ints
            indices = append(indexs, i)
        }
    }
    return indices
}

Playground: https://gotipplay.golang.org/p/EykXppG2qJa

Answered By – blackgreen

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.