web-dev-qa-db-ja.com

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

GoがT[]Tに暗黙的に変換するのに、Goが[]interface{}interface{}に暗黙的に変換しない理由を知りたいです。私が見逃しているこの変換について重要なことはありますか?

例:

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

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

go buildの苦情

関数の引数では、タイプ[] interface {}として(type [] string)を使用できません

そして、私がそれを明示的にしようとすると、同じこと:b := []interface{}(a)が不平を言います

(タイプ[] string)をタイプ[] interface {}に変換できません

そのため、この変換を行う必要があるたびに(多くの場合に発生するようです)、私はこのようなことをしています。

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

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

162
danny

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

この規則の1つの例外は、文字列の変換です。 string[]byteまたは[]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]
}
175

欠けているのは、Tの値を保持するTinterface{}がメモリ内で異なる表現を持っているため、簡単に変換できないことです。

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

タイプTの変数を保持するinterface{}は、このようにメモリ内で表されます

  • タイプTへのポインター

あなたの元の質問に戻ります:なぜgoは[]T[]interface{}に暗黙的に変換しないのですか?

[]T[]interface{}に変換するには、メモリ内のレイアウトが完全に異なるため、interface {}値の新しいスライスの作成が必要になります。

45
Nick Craig-Wood

公式の説明は次のとおりです。 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
}
10
Yandry Pozo

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

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

interface{}を任意の型に変換します。

構文:

result := interface.(datatype)

例:

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