将一个类型(int、float等)转换为`T` [go1.18]

huangapple go评论140阅读模式
英文:

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

问题

场景
支持泛型的Go 1.18

问题
无法将数字转换为泛型。

解释
我正在尝试将我的“通用目的”库转换为支持泛型。我遇到了数字转换错误的问题。

我定义了一个包,其中包含所有数字类型,如下所示:

package types

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

我没有特别的问题要处理泛型。我唯一没有理解的是:
如何将定义的类型(比如int)转换为泛型?

让我们假设以下示例:

// FindIndexValue是委托函数,用于获取给定值在输入数组中的索引。
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
}

在上面的代码片段中,错误出现在以下行:

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

这是因为range内置函数遍历数组并返回给定位置的索引(int类型)。

问题是:
如何将定义的类型(在本例中为int)转换为泛型T
错误:
cannot use i (variable of type int) as type T in argument to append

英文:

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

答案1

得分: 6

你可以像平常一样将一个值转换为参数类型。

根据提案类型转换

在一个具有两个类型参数 FromTo 的函数中,如果 From 的约束类型集中的所有类型都可以转换为 To 的约束类型集中的所有类型,那么可以将类型为 From 的值转换为类型为 To 的值。

在这种情况下,你实际上没有一个 From,因为它是切片索引 int;目标类型将是 T(尽管这可能不是你想要的,下面会解释为什么)。当然,int 可以转换为 T 的类型集,因为它只包括数值类型(尽管在 int8float64 的情况下可能会有截断或精度损失)。

indexs = append(indexs, T(i))

然而,你的程序将索引的切片声明为 []T,这意味着将泛型函数实例化为:

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

将得到一个类型为 []float64 的结果。由于返回的值是切片索引,它们始终是 int,这没有太多意义。

更好的做法是简单地返回 []int

func FindIndexValue[T Number](array []T, value T) []int {
    var indices []int
    for i := range array {
        // 匹配泛型类型的切片项
        if array[i] == value {
            // 将切片索引存储为 int
            indices = append(indexs, i)
        }
    }
    return indices
}

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

英文:

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

答案2

得分: 5

索引始终是整数类型(int),无论切片的元素类型如何。因此,结果类型应该是[]int

为什么你希望它是[]T呢?如果Tuint8,并且传入的切片包含超过256个元素,你甚至无法返回正确的索引。

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
}
英文:

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
}

huangapple
  • 本文由 发表于 2021年12月8日 19:40:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/70274394.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定