Go Interface Slice (Japanese)

"Interface Slice" from golang wiki

Go公式WikiのInterface Sliceの日本語意訳になります。
(元記事の最終更新日: 2014/12/10 rev.2)

[]interface{}に関する記述がされています。
これはGoを書き始めた当初、誰もがやろうとしてうまくできず疑問に感じる部分じゃないかと思います。


Interface Slice

はじめに

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{}型の変数はコンパイル時に判明する特定のメモリレイアウトを持っています。

スライス要素の各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
}
 
comments powered by Disqus