类型转换接口片


194

我很好奇为什么Go 不会隐式转换[]T[]interface{},而Go 会隐式转换Tinterface{}。我缺少这种转换的重要内容吗?

例:

func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello", "world"}
    foo(a)
}

go build 抱怨

不能在函数参数中使用(类型[]字符串)作为类型[] interface {}

如果我尝试明确执行此操作,则会遇到同样的事情:b := []interface{}(a)抱怨

无法将(类型[]字符串)转换为类型[] interface {}

因此,每次我需要进行这种转换时(这似乎很多),我一直在做这样的事情:

b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

是否有更好的方法来执行此操作,或者有标准的库函数来帮助进行这些转换?每次我想调用一个可以接收例如int或字符串列表的函数时,多写4行代码似乎很愚蠢。


因此,您定义“将[] T隐式转换为[] interface {}”意味着“分配新的切片并复制所有元素”。这与“将T隐式转换为interface {}”是矛盾的,后者只是与更通用的静态类型具有相同值的视图。什么也不会复制;如果您将其再次输入T,则仍然会得到相同的结果。
newacct 2012年

1
我并没有过多地考虑如何实现它,只是在更高层次上,能够轻松地将整个列表转换为另一种类型,作为不具有泛型类型的解决方法,将很方便。
丹尼2012年

Answers:


215

在Go中,有一个通用规则,即语法不应隐藏复杂/昂贵的操作。将a转换string为an interface{}需要O(1)时间。由于切片仍然是一个值[]stringinterface{}因此也需要O(1)时间将a转换为an 。但是,将a转换[]string为an的时间[]interface{}为O(n),因为切片的每个元素都必须转换为an interface{}

此规则的一个例外是转换字符串。当将a string与a []byte或a进行相互[]rune转换时,即使转换是“语法”,Go仍然可以执行O(n)。

没有标准的库函数可以为您完成此转换。您可以使用反射创建一个,但是它比三行选项要慢。

反射示例:

func InterfaceSlice(slice interface{}) []interface{} {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    ret := make([]interface{}, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = s.Index(i).Interface()
    }

    return ret
}

最好的选择是使用问题中给出的代码行:

b := make([]interface{}, len(a))
for i := range a {
    b[i] = a[i]
}

3
它可能会慢一些,但是可以在任何类型的切片上正常工作
newacct 2012年

整个答案适用于map
RickyA

这也同样适用channels
贾斯汀·欧姆斯

感谢您的明确解释,如果可以的话,您可以添加参考。为Converting a []string to an interface{} is also done in O(1) time
Mayur

56

您所缺少的是,T并且interface{}T在内存中具有不同的表示形式,因此不能轻易转换。

类型变量T就是它在内存中的值。没有关联的类型信息(在Go中,每个变量在编译时(而不是在运行时)都有一个已知的单一类型)。它在内存中的表示方式如下:

一个interface{}类型的保持变量T在这样存储器表示

  • 类型的指针 T

所以回到您原来的问题:为什么go不会隐式转换[]T[]interface{}

转换[]T[]interface{}会涉及创建新的interface {}值切片,这是不平凡的操作,因为内存布局完全不同。


10
这是内容丰富且写得很好的(+1),但您没有解决他的问题的另一部分:“是否有更好的方法来实现此目的...”(-1)。
weberc2 2014年


8

试试吧interface{}。要回切为片,请尝试

func foo(bar interface{}) {
    s := bar.([]string)
    // ...
}

3
这就引出了下一个问题:OP应该如何迭代bar以将其解释为“任何类型的切片”?请注意,他的三层划线会创建[]interface{},不是[]string或切片其他混凝土类型。
kostix 2012年

13
-1:仅在bar为[]字符串时有效,在这种情况下,您也可以这样写:func foo(bar []string) { /* ... */ }
weberc2 2014年

2
使用类型开关,或像接受的答案一样使用反射。
dskinner 2014年

OP将必须在运行时以任何方式弄清楚他的类型。使用non slice interface{}将使编译器在传递参数(如中)时不会抱怨foo(a)。他可以在其中使用反射或类型切换(golang.org/doc/effective_go.html#type_switchfoo
卡西奥格

2

转换interface{}为任何类型。

句法:

result := interface.(datatype)

例:

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.

2
你确定吗?我认为这是不正确的。您将看到它: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
Adriano Tadao

2

如果您需要进一步缩短代码,可以为助手创建新类型

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

然后

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.