编写一个函数,该函数接受任意切片作为参数。

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

Express function that takes any slice

问题

我想表达一个可以接受任意切片的函数。我原以为可以这样做:

func myFunc(list []interface{}) {
  for _, i := range list {
    ...
    some_other_fun(i)
    ...
  }
}

其中some_other_fun(..)本身接受interface{}类型。然而,这样做是行不通的,因为你不能将[]DEFINITE_TYPE作为[]interface{}传递。参见:https://golang.org/doc/faq#convert_slice_of_interface,其中指出[]interface{}的表示方式不同。这个答案总结了为什么不能将结构体赋值给接口的指针,但原因是相同的:https://stackoverflow.com/questions/13511203/why-cant-i-assign-a-struct-to-an-interface。

golang.org上提供的建议是从DEFINITE_TYPE切片重新构建一个新的接口切片。然而,在我想调用这个函数的代码的各个地方都这样做是不切实际的(这个函数本身只是为了缩短9行代码,但这9行代码在我们的代码中出现得相当频繁)。

在我想调用该函数的每种情况下,我都会传递一个[]*DEFINITE_TYPE,起初我以为这样会更容易抽象化,直到我再次发现https://stackoverflow.com/questions/13511203/why-cant-i-assign-a-struct-to-an-interface(上面也有链接)。

此外,每次我想调用该函数时,都会使用不同的DEFINITE_TYPE,因此为n种类型实现n个示例不会节省任何代码行数,也不会使我的代码更清晰(相反!)。

不能做到这一点真是令人沮丧,因为这9行代码在我们的代码中是惯用的,而且一个拼写错误很容易引入一个bug。我真的很想使用泛型。难道真的没有办法做到这一点吗?!!

英文:

I want to express a function that can take any slice. I thought that I could do this:

func myFunc(list []interface{}) {
  for _, i := range list {
    ...
    some_other_fun(i)
    ...
  }
}

where some_other_fun(..) itself takes an interface{} type. However, this doesn't work because you can't pass []DEFINITE_TYPE as []interface{}. See: https://golang.org/doc/faq#convert_slice_of_interface which notes that the representation of an []interface{} is different. This answer sums up why but with respect to pointers to interfaces instead of slices of interfaces, but the reason is the same: https://stackoverflow.com/questions/13511203/why-cant-i-assign-a-struct-to-an-interface.

The suggestion provided at the golang.org link above suggests rebuilding a new interface slice from the DEFINITE_TYPE slice. However, this is not practical to do everywhere in the code that I want to call this function (This function is itself meant to abbreviate only 9 lines of code, but those 9 lines appear quite frequently in our code).

In every case that I want to invoke the function I would be passing a []*DEFINITE_TYPE which I at first thought would be easier to abstract until, again, I discovered https://stackoverflow.com/questions/13511203/why-cant-i-assign-a-struct-to-an-interface (also linked above).

Further, everytime I want to invoke the function it is with a different DEFINITE_TYPE so implementing n examples for the n types would not save me any lines of code or make my code any clearer (quite the contrary!).

It is frustrating that I can't do this since the 9 lines are idiomatic in our code and a mistype could easily introduce a bug. I'm really missing generics. Is there really no way to do this?!!

答案1

得分: 9

在你提供的情况下,你需要将切片创建为interface类型的切片,例如s := []interface{}{}。这样你就可以将任何类型的元素放入切片中(甚至可以混合不同类型)。但是,你将不得不进行各种类型断言,这会变得非常复杂。

另一种常用的解析器常用的技术是定义如下:

func myFunc(list interface{})

因为切片适配了interface类型,所以你确实可以将普通的切片传递给它。你仍然需要在myFunc中进行一些验证和类型断言,但是你只需要对整个列表类型进行单一断言,而不必担心列表可能包含混合类型。

无论哪种方式,由于Go是一种静态类型语言,你最终必须通过断言来知道传入的类型。这就是事情的本质。在你的情况下,我可能会使用上述的函数签名,然后使用类型开关来处理不同的情况。参考这个文档:https://newfivefour.com/golang-interface-type-assertions-switch.html

所以,代码可能如下所示:

func myFunc(list interface{}) {
    switch v := list.(type) {
        case []string:
            // 处理字符串类型
        case []int32, []int64:
            // 处理整数类型
        case []SomeCustomType:
            // 处理SomeCustomType类型
        default:
            fmt.Println("未知类型")
    }
}
英文:

In the case you provided, you would have to create your slice as a slice of interface e.g. s := []interface{}{}. At which point you could literally put any type you wanted into the slice (even mixing types). But then you would have to do all sorts of type assertions and everything gets really nasty.

Another technique that is commonly used by unmarshalers is a definition like this:

func myFunc(list interface{})

Because a slice fits an interface, you can indeed pass a regular slice into this. You would still need to do some validation and type assertions in myFunc, but you would be doing single assertions on the entire list type, instead of having to worry about a list that could possibly contain mixed types.

Either way, due to being a statically typed language, you eventually have to know the type that is passed in via assertions. It's just the way things are. In your case, I would probably use the func signature as above, then use a type switch to handle the different cases. See this document https://newfivefour.com/golang-interface-type-assertions-switch.html

So, something like this:

func myFunc(list interface{}) {
    switch v := list.(type) {
        case []string:
            // do string thing
        case []int32, []int64:
            // do int thing
        case []SomeCustomType:
            // do SomeCustomType thing
        default:
            fmt.Println("unknown")
    }
}

答案2

得分: 4

没有,没有简单的方法来处理它。很多人在Go语言中想要泛型。

也许你可以参考sort.Sort函数和sort.Interface来找到一个合理的解决方案,而不需要复制切片。

英文:

No there is no easy way to deal with it. Many people miss generics in Go.

Maybe you can get inspired by sort.Sort function and sort.Interface to find a reasonable solution that would not require copying slices.

答案3

得分: 3

可能最好的做法是定义一个接口,封装myFunc需要对切片进行的操作(例如,在你的示例中,获取第n个元素)。然后,函数的参数就是该接口类型,并为要传递给函数的每种类型定义接口方法。

你也可以使用reflect包来实现,但这可能不是一个好主意,因为如果传递的不是切片(或数组或字符串),它会引发恐慌。

func myFunc(list interface{}) {
    listVal := reflect.ValueOf(list)
    for i := 0; i < listVal.Len(); i++ {
        //...
        some_other_fun(listVal.Index(i).Interface())
        //...
    }
}

参考:https://play.golang.org/p/TyzT3lBEjB。

英文:

Probably the best thing to do is to define an interface that encapsulates what myFunc needs to do with the slice (i.e., in your example, get the nth element). Then the argument to the function is that interface type and you define the interface method(s) for each type you want to pass to the function.

You can also do it with the reflect package, but that's probably not a great idea since it will panic if you pass something other than a slice (or array or string).

func myFunc(list interface{}) {
	listVal := reflect.ValueOf(list)
	for i := 0; i &lt; listVal.Len(); i++ {
		//...
		some_other_fun(listVal.Index(i).Interface())
		//...
	}
}

See https://play.golang.org/p/TyzT3lBEjB.

答案4

得分: 1

现在使用 Go 1.18+,你可以使用 generics 特性来实现这个功能:

func myFunc[T any](list []T) {
  for _, item := range list {
    doSomething(item)
  }
}
英文:

Now with Go 1.18+, you can use the generics feature to do that:

func myFunc[T any](list []T) {
  for _, item := range list {
    doSomething(item)
  }
}

huangapple
  • 本文由 发表于 2017年3月24日 05:40:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/42987535.html
匿名

发表评论

匿名网友

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

确定