Ответ 1
Как получить указатель на срез с использованием отражения
Простейшим решением, вероятно, является использование 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))