接口切片不是接口

505 阅读2分钟

前言

本文翻译自InterfaceSlice

由于在go中,我们可以将任意一种类型的变量赋值给interface{},所以常常有人写下如下的代码

var dataSlice []int = foo()
var interfaceSlice []interface{} = dataSlice

但是,此代码会报错,错误信息如下

cannot use dataSlice (type []int) as type []interface { } in assignment

这就会引出一个问题,为什么我可以赋值任意类型的变量给interface{},而不能将任意的切片赋值给[]interface{}?

为什么?

主要有两个原因。

首先,[]interface{}类型的变量不是一个interface{},它是一个切片,其元素凑巧正好为interface{}。即使给出如此解答,也有人会说[]interface{}的含义是非常明显的。

好吧,确实是这样的吗?在运行时,一个[]interface{}类型的变量有一种特定的内存结构。

每个interface{}会包含两个值,一个指的是此元素的类型,另一个指的是此元素的值或者一个指向此元素值的指针。因而,一个类型为[]interface{}、长度为N的切片,背后支撑的数据长度是2*N个值。

[]interface{}和长度相同、类型为[]MyType背后的数据量是不同的,[]MyType的数据块的长度是N*sizeof(MyType)

这就会造成你不能快速的将变量[]MyType赋值给[]interface{}。因为他们背后的数据并不相同。

应该怎样做?

这首先取决于你想怎么做。

如果你仅想要一个包含任意数组类型的容器,并且在把他们变回原来类型之前,不会使用索引的操作。你可以仅仅把[]MyType赋值给interface{}类型的变量。这种操作是非常普通和安全的。

如果你的确想要一个[]interface{},比如,在你想要在把变量变回原来类型之前,你需要使用索引;或者你使用的是一种特定类型的接口,需要使用接口的方法。那么你必须赋值一下切片。

var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
	interfaceSlice[i] = d
}