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