go - рефлексия - reflect type




Почему golang отражают. MakeSlice возвращает не адресуемое значение (2)

проверьте фрагмент ниже:

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

Почему это происходит? Потому что один из моих аргументов должен быть адресом среза.

Может быть, я не дал понять всем.

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

Приведенный выше код - вот почему мне нужен адрес среза.

переменная результата здесь - то, что мне нужно. Теперь обычно я могу сделать это

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

Но теперь SomeStruct является динамическим, и мне нужно создать срез с помощью refle.MakeSlice, так

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

И это ошибки: результат должен быть адресом среза.


Как получить указатель на срез, используя отражение

Самое простое решение, вероятно, состоит в том, чтобы использовать reflect.New() для создания указателя ( полный пример игры ):

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() .

Почему отражает. MakeSlice возвращает не адресуемое значение?

Свободное определение addressability в Go состоит в том, что вы можете взять адрес чего-либо, и вам гарантировано, что этот адрес указывает на что-то значимое. Если вы выделите что-то в стеке в теле функции, адрес назначенного значения в какой-то момент времени больше не будет доступен. Поэтому это значение не адресуется. В большинстве случаев Go перемещает локальные переменные стека в кучу, если они возвращаются или иным образом переносятся во внешнюю среду, но во время выполнения этого не делается. Следовательно, CanAddr() возвращает true только когда:

Значение является адресуемым, если оно является элементом среза, элементом адресуемого массива, полем адресуемой структуры или результатом разыменования указателя.

Все заявленные типы имеют одну общую черту: они гарантируют, что то, что у них есть, будет доступно везде и указывают на значимое значение в памяти. У вас нет ни элемента слайса, ни указателя, ни каких-либо других упомянутых вещей, поскольку вы создали локальный фрагмент с помощью reflect.MakeSlice . Тем не менее, элементы указанного среза будут адресуемыми (поскольку память среза находится в куче).

Почему указатель на срез?

Главный вопрос для меня в этом случае заключался в том, почему API mgo требует указателя на слайс для 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))

Я думаю, что вы здесь используете метод 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

Playground





go-reflect