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の一部です。