web-dev-qa-db-ja.com

golangreflect.MakeSliceがアドレス指定できない値を返す理由

以下のスニペットを確認してください。

http://play.golang.org/p/xusdITxgT-

なんでこんなことが起こっているの?私の議論の1つはスライスアドレスでなければならないからです。

多分私は皆のためにそれを明らかにしなかった。

collection.Find(bson.M{}).All(&result)

上記のコードは、スライスアドレスが必要な理由です。

ここでの結果変数は私が必要とするものです。今、私は通常これを行うことができます

result := make([]SomeStruct, 10, 10)

しかし、SomeStructは動的であり、reflect.MakeSliceを使用してスライスを作成する必要があります。

result := reflect.MakeSlice(reflect.SliceOf(SomeType))

そしてそれはエラーになります:結果はスライスアドレスでなければなりません。

21
castiel

リフレクションを使用してスライスへのポインタを取得する方法

最も簡単な解決策は、おそらくreflect.New()を使用してポインターを作成することです( playの完全な例 ):

_my := &My{}

// Create a slice to begin with
myType := reflect.TypeOf(my)
slice := reflect.MakeSlice(reflect.SliceOf(myType), 10, 10)

// Create a pointer to a slice value and set it to the slice
x := reflect.New(slice.Type())
x.Elem().Set(slice)

collection.Find(bson.M{}).All(x.Interface())
_

他の回答でも指摘されたx.Interface()に注意してください。これにより、_reflect.Value_の代わりにxの実際の値がAll()に渡されるのを防ぎます。

なぜreflect.MakeSliceがアドレス指定できない値を返すのですか?

Goでの addressability の大まかな定義は、何かのアドレスを取得でき、このアドレスが意味のある場所を指していることが保証されるということです。関数の本体のスタックに何かを割り当てると、割り当てられた値のアドレスは、ある時点でアクセスできなくなります。したがって、この値はアドレス指定できません。ほとんどの場合、Goは、ローカルスタック変数が返されるか、外部にプロモートされると、ヒープに移動しますが、実行時にはこれは行われません。したがって、 CanAddr() は、次の場合にtrueのみを返します。

値がスライスの要素、アドレス指定可能な配列の要素、アドレス指定可能な構造体のフィールド、またはポインターの逆参照の結果である場合、値はアドレス指定可能です。

記載されているタイプにはすべて共通点が1つあります。それは、保持しているものがどこからでもアクセス可能であり、メモリ内の意味のある値を指すことを保証することです。 _reflect.MakeSlice_を使用してローカルスライスを作成したため、スライスelement、ポインター、その他の言及されたものはありません。ただし、上記のスライスのelementsはアドレス指定可能です(スライスのメモリはヒープ上にあるため)。

なぜスライスへのポインタ?

この場合の私にとっての主な質問は、なぜ mgo のAPIが _iter.All_ のスライスへのポインターを必要とするのかということでした。結局のところ、スライスは参照型であり、提供されたデータセットを変更する場合、ポインターは必要ありません。しかし、ほとんどの場合、関数 がスライスにを追加する ということに気づきました。追加するとメモリが割り当てられ、メモリの割り当てによって古いデータが新しいメモリにコピーされます。新しいメモリとは、呼び出し元に伝達する必要のある新しいアドレスを意味します。

この動作は この例はプレイ中 に示されています。本質的に:

_// Works. Uses available storage of the slice.
    resultv.Index(1).Set(a)

// Executes but changes are lost:   
//  reflect.Append(resultv, a)

// Does not work: reflect.Value.Set using unaddressable value
//  resultv.Set(reflect.Append(resultv, a))
_
54
nemo

ここでは interface() メソッドを使用していると思いますが、reflectを使用して型のスライスバックを作成する必要がある理由がわかりません。

package main

import (
    "fmt"
    "reflect"
)

type My struct {
    Name string
    Id   int
}

func main() {
    my := &My{}
    myType := reflect.TypeOf(my)
    slice := reflect.MakeSlice(reflect.SliceOf(myType), 10, 10).Interface()
    p := slice.([]*My)

    fmt.Printf("%T %d %d\n", p, len(p), cap(p))
}

生産:

 [] * main.My 10 10 

遊び場

3
Intermernet