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
CodePudding user response:
The index is always integer type (int
) regardless of the element type of the slice. So result type should be []int
.
Why would you want it to be []T
? If T
is uint8
and the passed slice contains more than 256 elements, you couldn't even return the proper indices.
func FindIndexValue[T types.Number](array []T, value T) []int {
var indices []int
for i, v := range array {
if v == value {
indices = append(indices, i)
}
}
return indices
}
CodePudding user response:
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
andTo
, a value of typeFrom
may be converted to a value of typeTo
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