Go Wiki: InterfaceSlice

はじめに

任意の型の変数をinterface{}に代入できるため、多くの場合、次のようなコードを試みる人がいます。

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

これにより、次のエラーが発生します

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

そこで疑問が生じます。「任意の型をinterface{}に代入できるのに、なぜ任意のスライスを[]interface{}に代入できないのか?」

なぜ?

これには主に2つの理由があります。

第一に、[]interface{}型の変数はインターフェースではありません!これは、要素型がinterface{}であるスライスです。しかし、これさえあれば意味は明確だと言う人もいるかもしれません。

しかし、本当にそうでしょうか?[]interface{}型の変数は、コンパイル時に既知の特定のメモリレイアウトを持っています。

interface{}は2ワードを占めます(1ワードは含まれるものの型、もう1ワードは含まれるデータまたはそのポインタ)。その結果、長さ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の一部です。