Почему golang отражают.MakeSlice возвращает не адресуемое значение
Проверьте фрагмент ниже:
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))
И это ошибки: результат должен быть адресом среза.
2 ответа
Как получить указатель на срез, используя отражение
Самое простое решение, вероятно, заключается в использовании 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 возвращает не адресуемое значение?
Свободное определение адресуемости в 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