在使用Go语言编写的函数中,无法对列表类型的接口{}进行范围遍历。

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

Cannot Range Over List Type Interface {} In Function Using Go

问题

无法在使用Go的函数中对列表类型接口{}进行范围遍历。

  • 对我来说,在函数中执行范围遍历很重要。

如何修复?

package main

import (
	"fmt"
)

type MyBoxItem struct {
	Name string
}

type MyBox struct {
	Items []MyBoxItem
}

func (box *MyBox) AddItem(item MyBoxItem) []MyBoxItem {
	box.Items = append(box.Items, item)
	return box.Items
}

func PrintCustomArray(list interface{}) interface{} {
	items := list.([]MyBoxItem)
	for _, v := range items {
		fmt.Println(v.Name)
	}
	return nil
}

func main() {

	items := []MyBoxItem{}
	item := MyBoxItem{Name: "Test Item 1"}
	box := MyBox{items}
	box.AddItem(item)
	fmt.Println(box.Items)
	PrintCustomArray(box.Items)
}

错误:cannot range over list (type interface {})

如何修复?

英文:

Cannot Range Over List Type Interface {} In Function Using Go.

  • for me is important then i execute for in a function.

How can fix?

package main

import (
	"fmt"
)

type MyBoxItem struct {
	Name string
}

type MyBox struct {
	Items []MyBoxItem
}

func (box *MyBox) AddItem(item MyBoxItem) []MyBoxItem {
	box.Items = append(box.Items, item)
	return box.Items
}

func PrintCustomArray(list interface{}) interface{}  {
	//items := reflect.ValueOf(list)
	for _, v := range list {
		fmt.Println(v.Key,v.Value)
	}
	return 0
}
func main() {

	items := []MyBoxItem{}
	item := MyBoxItem{Name: "Test Item 1"}
	box := MyBox{items}
	box.AddItem(item)
	fmt.Println((box.Items))
	PrintCustomArray(box.Items)
}

https://play.golang.org/p/ZcIBLMliq3

Error : cannot range over list (type interface {})

How can fix?

答案1

得分: 5

注意

下面的答案以广义上的两种可能方法为基础:使用接口和使用特定类型。提到接口的方法是为了完整起见。在我看来,你提出的情况不适合使用接口。

下面是一个使用这两种技术的示例链接。对于这个特定情况来说,任何人都可以明显地看出接口方法太繁琐了。


除了你似乎对Go中的循环不太熟悉(例如,v.Keyv.Value是不存在的字段),我将尝试回答你的问题。

你确实将一个列表传递给了函数,但它被处理为interface{}类型。这意味着你的函数接受任何值作为参数。你不能简单地对它们进行迭代。

你可以使用类型断言将参数转换为切片,然后使用另一个断言将其作为另一个特定接口使用:

type Item interface {
    key() string
    val() string
}

func (i MyBoxItem) key() string {
    return i.Key
}

func (i MyBoxItem) val() string {
    return i.Value
}

func PrintCustomArray(list interface{}) error {
    listSlice, ok := list.([]interface{})
    if !ok {
        return fmt.Errorf("Argument is not a slice")
    }
    for _, v := range listSlice {
        item, ok := v.(Item)
        if !ok {
            return fmt.Errorf("element in slice does not implement the Item interface")
        }
        fmt.Println(item.key(), item.val())
    }
    return nil
}

但是,说实话,像这样的函数只有在传递一个切片作为参数时才起作用。因此,在这里加入第一个类型断言毫无意义。至少,将函数更改为以下内容更有意义:

func PrintCustomArray(list []interface{})

然后,因为我们不期望传递一个数组,而是一个切片,所以名称应该更改为PrintCustomSlice

最后,因为我们在切片中的每个值上都使用相同的类型断言,我们可以进一步改变函数:

// 在这一点上,我们将始终返回0,这是没有意义的
// 只需不返回任何内容
func PrintCustomSlice(list []Item) {
    for _, v := range list {
        fmt.Println(v.key(), v.val())
    }
}

这样的函数的优点是它仍然可以处理多种类型(你只需要实现接口)。你不需要任何昂贵的操作(如反射)或类型断言。

类型断言非常有用,但在这种情况下,它们只是用来隐藏本应导致编译时错误的问题。Go的interface{}类型是非常有用的,但你似乎在利用它来规避类型系统。如果这是你想要实现的目标,为什么要使用有类型的语言呢?

一些结束的想法/备注:如果你的函数只用于迭代特定的“东西”,你根本不需要接口,只需在函数中指定你期望传递的类型。在这种情况下,可以这样写:

func PrintCustomSlice(list []MyBoxItem) {
    for _, v := range list {
        fmt.Println(v.Key, v.Value)
    }
}

我还注意到你似乎将所有东西都导出了(所有函数、类型和字段都以大写字母开头)。在Go中,这被认为是不好的形式。只导出需要公开的内容。在main包中,通常只会导出很少的内容。

最后,正如我在开始时提到的:你似乎对基础知识还不够了解。我强烈建议你完成交互式教程。它很好地介绍了基础知识,并以适当的速度展示了语言的特性。完成它不会花费很长时间,但一定是值得花几个小时的时间完成的。


Playground 演示

英文:

Note

The answer below describes, in broad strokes, 2 possible approaches: using interfaces, and using specific types. The approach focusing on interfaces is mentioned for completeness sake. IMHO, the case you've presented is not a viable use-case for interfaces.

Below, you'll find a link to a playground example that uses both techniques. It should be apparent to anyone that the interface approach is too cumbersome if for this specific case.


Quite apart from the fact that you don't really seem to be too familiar with how loops work in go (v.Key and v.Value are non-existent fields for example), I'll attempt to answer your question.

You are passing a list to your function, sure enough, but it's being handled as an interface{} type. That means your function accepts, essentially, any value as an argument. You can't simply iterate over them.

What you can do is use type assertions to convert the argument to a slice, then another assertion to use it as another, specific interface:

type Item interface{
    key() string
    val() string
}

func (i MyBoxItem) key() string {
    return i.Key
}

func (i MyBoxItem) val() string {
    return i.Value
}

func PrintCustomArray(list interface{}) error  {
    listSlice, ok := list.([]interface{})
    if !ok {
        return fmt.Errorf("Argument is not a slice")
    }
    for _, v := range listSlice {
        item, ok := v.(Item)
        if !ok {
            return fmt.Errorf("element in slice does not implement the Item interface")
        }
        fmt.Println(item.key(), item.val())
    }
    return nil
}

But let's be honest, a function like this only works if a slice is passed as an argument. So having that first type assertion in there makes no sense whatsoever. At the very least, changing the function to something like this makes a lot more sense:

func PrintCustomArray(list []interface{})

Then, because we're not expecting an array as such, but rather a slice, the name should be changed to PrintCustomSlice.<br>
Lastly, because we're using the same type assertion for every value in the slice, we might as well change the function even more:

// at this point, we&#39;ll always return 0, which is pointless
// just don&#39;t return anything
func PrintCustomSlice(list []Item) {
    for _, v := range list {
        fmt.Println(v.key(), v.val())
    }
}

The advantages of a function like this is that it can still handle multiple types (all you have to do is implement the interface). You don't need any kind of expensive operations (like reflection), or type assertions.

Type assertions are very useful, but in a case like this, they merely serve to hide problems that would otherwise have resulted in a compile-time error. Go's interface{} type is a very useful thing, but you seem to be using it to get around the type system. If that's what you want to achieve, why use a typed language in the first place?

Some closing thoughts/remarks: If your function is only going to be used to iterate over specific "thing", you don't need the interfaces at all, simply specify the type you're expecting to be passed to the function in the first place. In this case that would be:

func PrintCustomSlice(list []MyBoxItem) {
    for _, v := range list {
        fmt.Println(v.Key, v.Value)
    }
}

Another thing that I've noticed is that you seem to be exporting everything (all functions, types, and fields start with a capital letter). This, in go, is considered bad form. Only export what needs to be public. In the main package, that usually means you're hardly export anything.

Lastly, as I mentioned at the start: you don't seem to have a firm grasp on the basics just yet. I'd strongly recommend you go through the interactive tour. It covers the basics nicely, but shows you the features of the language at a decent pace. It doesn't take long, and is well worth taking a couple of hours to complete


Playground demo

答案2

得分: 2

可以使用reflect包来实现PrintCustomArray,但大多数有经验的Go程序员会使用简单的for循环来编写:

for _, i := range box.Items {
    fmt.Println("Name:", i.Name)
}

你也可以将其封装在一个函数中:

func PrintCustomArray(items []MyBoxItem) {
    for _, i := range items {
        fmt.Println("Name:", i.Name)
    }
}

链接:https://play.golang.org/p/c4EPQIx1AH

英文:

It's possible to implement PrintCustomArray using the reflect package, but most experienced Go programmers will write a simple for loop:

for _, i := range box.Items {
	fmt.Println(&quot;Name:&quot;, i.Name)
}

https://play.golang.org/p/RhubiCpry0

You can also encapsulate it in a function:

func PrintCustomArray(items []MyBoxItem) {
  for _, i := range items {
	fmt.Println(&quot;Name:&quot;, i.Name)
  }
}

https://play.golang.org/p/c4EPQIx1AH

答案3

得分: 0

由于你要求只返回翻译好的部分,以下是翻译的内容:

AddItem()函数中,由于你返回了box.Items,所以Items的类型应该是[]MyBoxItem,因此列表的类型应该是[]MyBoxItem。此外,在PrintCustomArray函数中,你返回了0,并且设置的返回类型是{}interface

func PrintCustomArray(list []MyBoxItem) {
    //items := reflect.ValueOf(list)
    for i, v := range list {
        fmt.Println(i, v)
    }
    //return 0
}

另外,MyBoxItem结构体只有一个名为Name的变量,所以v.keyv.value没有任何意义。

以下是正确的代码示例:https://play.golang.org/p/ILoUwEWv6Y。

你需要搞清楚Go语言中的接口。这篇文章可能会有所帮助:https://golang.org/doc/effective_go.html#interfaces_and_types。

英文:

Here since you are returning box.Items from AddItem(), Items is of the type []MyBoxItem , so list should be of type []MyBoxItem .Moreover you are returning 0 in PrintCustomArray and the return type you have set is {}interface.

func PrintCustomArray(list []MyBoxItem) {
    //items := reflect.ValueOf(list)
	for i, v := range list {
    	fmt.Println(i, v)
    }
    //return 0
}

Again, MyBoxItem struct has only one variable named Name so v.key v.value won't make any sense.

This is what the proper code should look like https://play.golang.org/p/ILoUwEWv6Y .

You need to clear your understanding about interfaces in go. This might help https://golang.org/doc/effective_go.html#interfaces_and_types .

huangapple
  • 本文由 发表于 2017年2月6日 00:09:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/42054248.html
匿名

发表评论

匿名网友

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

确定