英文:
creating generic functions for multi type arrays in Go
问题
我正在尝试创建一个通用函数,可以处理 Go 中的切片操作...例如,将任何类型的项附加到相同类型的切片中。这只是一个更复杂解决方案的通用目的,但总体问题归结为以下示例:
package main
type car struct {
make string
color string
}
type submarine struct {
name string
length int
}
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
func main() {
cars := make([]car, 0, 10)
cars[0] = car{make: "ford", color: "red"}
cars[1] = car{make: "chevy", color: "blue"}
subs := make([]submarine, 0, 10)
subs[0] = submarine{name: "sally", length: 100}
subs[1] = submarine{name: "matilda", length: 200}
newCar := car{make: "bmw", color: "white"}
genericAppender(&cars, newCar)
}
代码播放器位于此位置。
以上代码出现以下错误:
prog.go:14: first argument to append must be slice; have interface {}
英文:
I am trying to create a generic function that can handle actions on slices in Go... for instance, append an item of any type to a slice of that same type. This is simply a generic purpose for a more complex solution, but overall the issue boils down to this example:
package main
type car struct {
make string
color string
}
type submarine struct {
name string
length int
}
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
func main() {
cars := make([]car, 0, 10)
cars[0] = car{make: "ford", color: "red"}
cars[1] = car{make: "chevy", color: "blue"}
subs := make([]submarine, 0, 10)
subs[0] = submarine{name: "sally", length: 100}
subs[1] = submarine{name: "matilda", length: 200}
newCar := car{make: "bmw", color: "white"}
genericAppender(&cars, newCar)
}
The code playground is at this location
The above errors as follows:
prog.go:14: first argument to append must be slice; have interface {}
答案1
得分: 4
在这个更改之后,你仍然遇到了一个运行时错误(索引超出范围),然而问题是thingList
的类型不是[]interface{}
,而是interface{}
,所以你无法向其追加元素。下面是你的代码在playground上更新后的版本,它在追加操作之前进行了类型断言,将其转换为[]interface{}
。实际上,你需要在单独的一行进行类型断言并检查错误。
https://play.golang.org/p/YMed0VDZrv
所以在这里放一些代码:
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList.([]interface{}), thing)
}
将解决你所面临的基本问题。如前所述,当对切片进行索引操作时仍然会出现运行时错误。此外,你可以通过修改参数来避免这个问题,使其变为:
func genericAppender(thingList []interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
这里有一个完整的第二种类型的示例:https://play.golang.org/p/dIuW_UG7XY
请注意,我还纠正了运行时错误。当你使用带有3个参数的make函数时,它们的顺序是类型、长度、容量。这意味着数组的长度为0,所以当你尝试分配给索引0和1时,会导致IndexOutOfRange的恐慌。相反,我删除了中间的参数,所以它变成了make([]interface{}, 10)
,意味着长度最初设置为10,因此你可以对这些索引进行赋值。
英文:
After this change you're still getting a runtime error (index out of range) however the problem is that thingList
is not of type []interface{}
but rather interface{}
so you can't append to it. Here's an updated version of your code on playground that does a type assertion to convert it to an []interface{}
in line with the append. In reality you need to do that on a separate line and check for errors.
https://play.golang.org/p/YMed0VDZrv
So to put some code here;
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList.([]interface{}), thing)
}
will solve the basic problem you're facing. As noted, you still get runtime errors when indexing into the slice. Also, you could change the argument to avoid this by making it;
func genericAppender(thingList []interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
Here's a complete example of the second type; https://play.golang.org/p/dIuW_UG7XY
Note I also corrected the runtime error. When you use make with 3 args they are, in this order, type, length, capacity. This means the length of the array is 0 so when you try to assign to indexes 0 and 1 it was causing a panic for IndexOutoFRange. Instead I removed the middle argument so it's make([]interface{}, 10)
meaning the length is initially set to 10 so you can assign to those indexes.
答案2
得分: 1
在上面的答案中,如果按照以下方式操作,则会出现错误。这就是原始问题的关键所在:
//genericAppender(subs, newCar). // Throws "cannot use subs (type []submarine) as type []interface {} in argument to genericAppender"
关键是将特定类型的切片转换为通用的[]interface{}。
func convertToGeneric(thingList interface{}) []interface{} {
input := reflect.ValueOf(thingList)
length := input.Len()
out := make([]interface{},length)
for i:=0 ;i < length; i++ {
out[i] = input.Index(i).Interface()
}
return out
}
你可以这样调用该函数:
genericAppender(convertToGeneric(subs), newCar)
你可以在这里查看修改后的可工作代码:https://play.golang.org/p/0_Zmme3c8lT
英文:
In the answer above if you do the following then it throws error. This is what the original question was about:
//genericAppender(subs, newCar). // Throws "cannot use subs (type []submarine) as type []interface {} in argument to genericAppender"
The trick is to convert your slice of specific type into a generic []interface{}.
func convertToGeneric(thingList interface{}) []interface{} {
input := reflect.ValueOf(thingList)
length := input.Len()
out := make([]interface{},length)
for i:=0 ;i < length; i++ {
out[i] = input.Index(i).Interface()
}
return out
}
This you can call the function like this:
genericAppender(convertToGeneric(subs), newCar)
You can check modified working code here: https://play.golang.org/p/0_Zmme3c8lT
答案3
得分: 0
从Go 1.19(2022年第四季度)开始,不再需要接口或将特定类型的切片转换为通用的[]interface{}
。
CL 363434引入了一个新的slices包:
// Package slices定义了与任意类型的切片一起使用的各种有用的函数。
// 除非另有说明,这些函数都适用于索引0 <= i < len(s)的切片元素。
package slices
import "constraints"
// Grow增加切片的容量,如果需要,以保证另外n个元素的空间。
// 在Grow(n)之后,可以附加至少n个元素到切片中而不需要另外分配内存。
// 如果n为负数或太大而无法分配内存,则Grow会引发panic。
func Grow[S ~[]T, T any](s S, n int) S {
return append(s, make(S, n)...)[:len(s)]
}
// Equal报告两个切片是否相等:长度相同且所有元素相等。
// 如果长度不同,则Equal返回false。
// 否则,按索引顺序比较元素,并在第一对不相等的元素处停止比较。
// 浮点数NaN不被视为相等。
func Equal[T comparable](s1, s2 []T) bool {
if len(s1) != len(s2) {
return false
}
for i, v1 := range s1 {
v2 := s2[i]
if v1 != v2 {
return false
}
}
return true
}
// ...
Ian Lance Taylor在issue 45955中确认:
> 此包现在可在golang.org/x/exp/slices
中使用。
根据此线程,它将不会在1.19版本之前放入标准库。
当然,我们可能会根据在x/exp
中使用它时了解到的任何情况进行调整。
英文:
With Go 1.19 (Q4 2022), no need for interface, or "convert your slice of specific type into a generic []interface{}
"
CL 363434 comes with a new slices packages:
// Package slices defines various functions useful with slices of any type.
// Unless otherwise specified, these functions all apply to the elements
// of a slice at index 0 <= i < len(s).
package slices
import "constraints"
// Grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. If n is negative or too large to
// allocate the memory, Grow panics.
func Grow[S ~[]T, T any](s S, n int) S {
return append(s, make(S, n)...)[:len(s)]
}
// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[T comparable](s1, s2 []T) bool {
if len(s1) != len(s2) {
return false
}
for i, v1 := range s1 {
v2 := s2[i]
if v1 != v2 {
return false
}
}
return true
}
// ...
Ian Lance Taylor confirms in issue 45955:
> This package is now available at golang.org/x/exp/slices
.
Per this thread, it will not be put into standard library until the 1.19 release.
We may of course adjust it based on anything we learn about having it in x/exp
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论