Почему 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

Детская площадка

Другие вопросы по тегам