Golang 中的泛型类型转换

use*_*572 5 generics type-conversion go

我厌倦了编写像 []int32 -> []int64 这样的手动切片转换,因为在很多情况下您需要将切片与其他类型一起使用。所以我尝试为此编写一个通用函数:

func convertSlice[T1 any, T2 any](t1 []T1) []T2 {
    t2 := make([]T2, len(t1))
    for i := range t1 {
        t2[i] = T2(t1[i])
    }
    return t2
}
Run Code Online (Sandbox Code Playgroud)

并想像这样使用它

a := []int{1, 2, 3, 4, 5}
var b []int64 = convertSlice[int, int64](a)
Run Code Online (Sandbox Code Playgroud)

但我无法编译它,编译器说./prog.go:8:14: cannot convert t1[i] (variable of type T1 constrained by any) to type T2

那么,我该如何解决这个问题呢?

实例: https: //go.dev/play/p/YYOLFjYt4mq

当然,我可以为每个基本类型编写单独的函数,例如:

func convertNumericSlice[T1, T2 constraints.Integer | constraints.Float](t1 []T1) []T2 {}
func convertStringSlice[T1, T2 ~string](t1 []T1) []T2 {}
Run Code Online (Sandbox Code Playgroud)

但这看起来根本不像很酷的通用方式。

bla*_*een 2

无法转换具有任意约束的类型参数。规范提到(转换):

\n
\n

[...] 如果满足以下条件之一,x 也可以转换为 T 类型:

\n
    \n
  • V 和 T 都是类型参数, V 的类型集中的每种类型的值都可以转换为 T 的类型集中的每种类型。
  • \n
\n
\n

如果 和T1T2受到 的约束any,则两个类型集实际上都包含任何可能的类型。

\n

即使convertSlice函数的特定实例化可能有效,编译器也无法静态证明转换始终T2(t1)有效。理论上,您可以实例化,然后字符串显然无法转换为函数通道。convertSlice[string, chan func()]

\n

编写 catch-all 函数的唯一方法是在CanConvert方法中使用反射,但您将需要决定如何处理CanConvertreturns 的情况false。恐慌?返回零值?

\n

如果您选择使用泛型,那么您就已经做出了类型安全的选择,因此引入反射似乎是违反直觉的。对于泛型,您必须为每组可转换基础类型编写函数。主要特殊情况有:

\n

数字:

\n
type Number interface {\n    constraints.Integer | constraints.Float\n}\n\nconvertNumbers[T1, T2 Number](t1 []T1) []T2 {}\n
Run Code Online (Sandbox Code Playgroud)\n

字符串 \xe2\x80\x94 它是字节切片和符文切片的特殊情况,但字节切片和符文切片不能相互转换,所以这里需要两个函数):

\n
convertStrings[T1, T2 ~string | ~[]byte](t1 []T1) []T2 {}\n// or\nconvertStrings[T1, T2 ~string | ~[]rune](t1 []T1) []T2 {}\n
Run Code Online (Sandbox Code Playgroud)\n

复杂的:

\n
convertComplex[T1, T2 ~complex64 | ~complex128](t1 []T1) []T2 {}\n
Run Code Online (Sandbox Code Playgroud)\n