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つの理由があります。

1つ目は、[]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の一部です。