Go Wiki: InterfaceSlice

引言

考虑到任何类型的变量都可以赋给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{}占用两个字(一个字用于存储内容的类型,另一个字用于存储内容本身或指向内容的指针)。因此,长度为 N 的[]interface{}类型的切片由 N*2 个字的内存块支持。

这与类型为[]MyType且长度相同的切片所支持的内存块不同。它的内存块将是 N*sizeof(MyType) 个字长。

结果是,您无法快速将[]MyType类型的东西赋给[]interface{}类型的东西;它们后面的数据看起来就是不同的。

我还能做什么?

这取决于您最初想做什么。

如果您想要一个任意数组类型的容器,并且打算在进行任何索引操作之前将其转换回原始类型,那么您可以使用interface{}。代码将是通用的(如果不是编译时类型安全的)并且速度很快。

如果您确实想要一个[]interface{},因为您将在转换回之前进行索引操作,或者您正在使用特定的接口类型并想使用其方法,那么您将不得不复制该切片。

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

此内容是 Go Wiki 的一部分。