在Go语言中的可变泛型参数

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

Variadic generic arguments in Go

问题

让我们假设我想在Go语言中为切片创建与JavaScript的Array.splice函数相当的功能。我有以下代码:

func splice(slice []int, index, amount int, elements ...int) []int {
    newslice := make([]int, 0)
    for i := 0; i < index; i++ {
        newslice = append(newslice, slice[i])
    }
    for i := index + amount; i < len(slice); i++ {
        newslice = append(newslice, slice[i])
    }
    for _, el := range elements {
        newslice = append(newslice, el)
    }
    return newslice
}

这个例子可以工作,但只适用于int类型的参数。我想要使它通用化,并且我知道我应该给可变参数elements指定类型interface{},但是我如何在函数内部根据参数的类型创建一个具有该接口类型的新切片呢?

换句话说,在函数的第一行中创建newslice时,我如何根据参数的类型动态指定切片的类型?

英文:

Let's say I want to make the equivalent of the JavaScript Array.splice function in Go, for Slices. I have the following code:

func splice(slice []int, index, amount int, elements ...int) []int {
    newslice := make([]int, 0)
    for i := 0; i &lt; index; i++ {
        newslice = append(newslice, slice[i])
    }
    for i := index + amount; i &lt; len(slice); i++ {
        newslice = append(newslice, slice[i])
    }
    for _, el := range elements {
        newslice = append(newslice, el)
    }
    return newslice
}

This example will work, but only for arguments of type int. I want to make it generic, and I know that I should give the variadic argument elements the type interface{}, but how do I create a new slice with the type of that interface from inside the function?

In other words, how can I specify the type of the slice dynamically depending on the type of the arguments in the first line of the function, where newslice is created?

答案1

得分: 6

使用反射

如果你真的想要做一些通用的事情,反射是最终的答案。
请参阅反射包中的MakeSlice文档以了解有关您的问题的详细信息。

您只需要获取传入切片的类型(使用TypeOf(...)),然后正确应用MakeSlice

使用反射创建切片的示例:

y := []int{1,2,3}
t := reflect.TypeOf(y)

slice := reflect.MakeSlice(t, 0, 10)
slice = reflect.Append(slice, reflect.ValueOf(2))

fmt.Println(slice.Interface())

在此运行

使用[]interface{}

另一种处理的方式是使用[]interface{},它可以存储任何值,但可能会导致运行时恐慌,因为您完全省略了编译器类型检查(这是一件坏事)。

这里是一个使用[]interface{}作为任意值存储的示例。使用此方法,您不需要在切片实现中知道类型,只需切片并使用[]interface{}来创建新的切片。

这种方法的缺点是,您无法轻松地将某些切片转换为[]interface{}。您必须手动复制它,如之前的帖子中所述

结论

无论您使用哪个版本,如果不知道类型并手动将其转换回来,您将永远无法恢复类型安全性。在Go中没有这样的东西会为您做到这一点。这意味着,在您的代码中,您将有类似以下的内容来恢复类型安全性:

x := []int{1,2,3,4}
y := splice(x, ...)
yn := []int(y)
英文:

Using reflection

If you really want to do generic stuff, reflection is the ultimate answer.
See the MakeSlice documentation
in the reflection package for details on your problem.

You just need to retrieve the type of the incoming slice (using TypeOf(...))
and applying MakeSlice correctly.

Example of using reflection to create a slice:

y := []int{1,2,3}
t := reflect.TypeOf(y)

slice := reflect.MakeSlice(t, 0, 10)
slice = reflect.Append(slice, reflect.ValueOf(2))

fmt.Println(slice.Interface())

Run it here.

Using []interface{}

Another way to work with, is []interface{}, which can store any value
but may lead to runtime panics as you omit compiler type checking completely
(this is a bad thing).

Here is an example for using []interface{}
as storage for arbitrary values. With this you don't need to know the type in
your splice implementation, you just splice and use []interface{} for new slices.

This method has the drawback, that you can't convert some slice to []interface{} easily. You have to copy it manually, as described in posts before.

Conclusion

Regardless of which version you use, you will never get back type safety without
knowing the type and converting it back manually. There's no such thing in Go
which will do that for you. That means, that you'll have something like this
in your code to regain type safety:

x := []int{1,2,3,4}
y := splice(x, ...)
yn := []int(y)

答案2

得分: 5

不要在Go中模拟JavaScript(为什么???),我想建议使用SliceTricks的构建块来组合类似的所需操作。

它们具有以下特点:

  • 完全与类型无关(免费使用“泛型”)。
  • 与在[]interface{}中打包/解包相比,可能更快。
英文:

Instead of emulating JavaScript in Go (why ???) I would like to suggest to compose simmilar required operations from the building blocks of SliceTricks.

They are:

  • Completely type agnostic (think "generics" for free).
  • Quite probably pretty faster compared to packing/unpacking whatsoever in/from a []interface{}.

huangapple
  • 本文由 发表于 2012年12月7日 06:14:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/13753603.html
匿名

发表评论

匿名网友

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

确定