インターフェイスのスライスの型変換


194

囲碁does'tが暗黙的に変換し、なぜ私が興味[]T[]interface{}それは暗黙的に変換されますときTinterface{}。私が見逃しているこの変換について重要なものはありますか?

例:

func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello", "world"}
    foo(a)
}

go build 文句を言う

(型[]文字列)を関数引数の型[]インターフェイス{}として使用することはできません

そして私がそれを明示的にやろうとすると、同じこと:b := []interface{}(a)文句を言う

(タイプ[]文字列)をタイプ[]インターフェース{}に変換できません

したがって、私はこの変換を行う必要があるたびに(これは非常に多く現れるようです)、次のようなことをしています。

b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

これを行うより良い方法、またはこれらの変換を支援する標準ライブラリ関数はありますか?intや文字列などのリストを取得できる関数を呼び出すたびに、コードを4行追加するのはばかげているようです。


したがって、「暗黙的に[] Tを[] interface {}に変換する」と定義して、「新しいスライスを割り当て、すべての要素をコピーする」ことを意味します。これは、 "Tを暗黙的にインターフェイスに変換する{}"の動作と一致していません。これは、より一般的な静的型と同じ値のビューにすぎません。何もコピーされません。タイプアサートしてT型に戻しても、同じ結果が得られます。
newacct 2012年

1
それがどのように実装されるかについてあまり詳細に考えていませんでした。より高いレベルでは、ジェネリック型を持たないための回避策としてリスト全体を別の型に簡単に変換できると便利です。
danny

回答:


215

Goでは、構文によって複雑でコストのかかる操作が隠されないようにするという一般的なルールがあります。a stringからanへの変換interface{}はO(1)時間で行われます。スライスはまだ1つの値なので、a []stringからanへの変換interface{}もO(1)時間で行われます。ただし、スライスの各要素をに変換する必要があるため、a []stringをに変換する[]interface{}とO(n)時間になりますinterface{}

このルールの1つの例外は、文字列の変換です。a stringをa []byteまたはa []runeに変換する場合、変換が「構文」であっても、GoはO(n)を機能させます。

この変換を行う標準ライブラリ関数はありません。リフレクトを使用して作成することもできますが、3行のオプションよりも遅くなります。

リフレクションの例:

func InterfaceSlice(slice interface{}) []interface{} {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    ret := make([]interface{}, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = s.Index(i).Interface()
    }

    return ret
}

ただし、質問で指定したコード行を使用するのが最善の方法です。

b := make([]interface{}, len(a))
for i := range a {
    b[i] = a[i]
}

3
遅いかもしれませんが、一般的にはどのタイプのスライスでも機能します
newacct

この答え全体はmaps too btwにも当てはまります。
RickyA 2016年

これは同様に適用さchannelsれます。
ジャスティンオーム2017

明確な説明に感謝します。可能であれば、refを追加できます。のためにConverting a []string to an interface{} is also done in O(1) time
Mayur

56

あなたが不足している事は、ということですTし、interface{}その値を保持しているTメモリを持っている別の表現をそれほど自明変換することはできません。

タイプの変数はT、メモリ内の単なる値です。関連付けられた型情報はありません(Goでは、すべての変数が実行時ではなくコンパイル時に認識される単一の型を持っています)。これは、次のようにメモリで表現されます。

interface{}型の変数を保持することはT、このようなメモリで表現されます

  • タイプへのポインタ T

元の質問に戻ってください:なぜ暗黙的に変換[]T[]interface{}ないのですか?

変換[]Tには[]interface{}、新しいスライス作成含むであろうinterface {}メモリ内のレイアウトが完全に異なるため、非自明な動作である値。


10
これは有益でよく書かれています(+1)が、彼の質問の他の部分、「これを行うより良い方法はありますか?」(-1)には触れていません。
weberc2 2014

13

これが公式の説明です:https : //github.com/golang/go/wiki/InterfaceSlice

var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
    interfaceSlice[i] = d
}

良い情報ですが、公式の説明ではありません。誰でも編集できるwikiです。
イナンクムス

どのように[]interface私は期待するようなタイプに変換する必要があり[]map[string]interface{}ますか?
ルイスチャン

8

interface{}代わりに試してください。スライスとしてキャストし直すには、

func foo(bar interface{}) {
    s := bar.([]string)
    // ...
}

3
これは、次の質問を頼みます:OP barを「任意のタイプのスライス」として解釈するために、OPがどのように反復されるのですか?彼の3ライナーが[]interface{}、ない、[]stringまたは他のコンクリートタイプのスライスを作成することに注意してください。
kostix

13
-1:バーは、あなたにも書き込むことができ、その場合には、[]の文字列の場合にのみ動作します:func foo(bar []string) { /* ... */ }
weberc2

2
タイプスイッチを使用するか、受け入れられた回答のようにリフレクションを使用します。
dskinner 2014

OPは、実行時に何らかの方法で彼のタイプを把握する必要があります。非スライスinterface{}を使用すると、のように引数を渡すときにコンパイラが文句を言わなくなりfoo(a)ます。彼は内部でリフレクトまたはタイプスイッチング(golang.org/doc/effective_go.html#type_switch)を使用できましたfoo
Cassiohg

2

interface{}任意のタイプに変換します。

構文:

result := interface.(datatype)

例:

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.

2
それ本当?これは有効ではないと思います。あなたはそれを見るつもりです: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
アドリアーノ忠夫

2

コードをさらに短くする必要がある場合は、ヘルパーの新しい型を作成できます

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

その後

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.