¿Por qué golang reflect.MakeSlice devuelve un valor no direccionable?
reflection go-reflect (2)
Cómo conseguir un puntero a un corte utilizando la reflexión.
La solución más sencilla es probablemente usar reflect.New()
para crear el puntero ( ejemplo completo en juego ):
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())
Note el x.Interface()
que también fue señalado por otras respuestas. Esto evita que en lugar de reflect.Value
el valor real de x
se pasa a All()
.
¿Por qué reflect.MakeSlice devuelve un valor no direccionable?
Una definición flexible de addressability en Go es que puede tomar la dirección de algo y tiene la garantía de que esta dirección apunta a un lugar significativo. Si asigna algo en la pila en el cuerpo de una función, la dirección del valor asignado, en algún momento, ya no será accesible. Por lo tanto, este valor no es direccionable. En la mayoría de los casos, Go mueve las variables de la pila local al montón si se devuelven o se promueven de otra manera al exterior, pero en tiempo de ejecución no se hace. Por lo tanto, CanAddr()
solo devuelve true
cuando:
Un valor es direccionable si es un elemento de un sector, un elemento de una matriz direccionable, un campo de una estructura direccionable o el resultado de la desreferenciación de un puntero.
Todos los tipos mencionados tienen una cosa en común: garantizan que lo que poseen será accesible desde cualquier lugar y apuntan a un valor significativo en la memoria. No tiene un elemento de sector , ni un puntero, ni ninguna de las otras cosas mencionadas, ya que creó un sector local utilizando reflect.MakeSlice
. Sin embargo, los elementos de dicho segmento serían direccionables (ya que la memoria del segmento reside en el montón).
¿Por qué un puntero a una rebanada?
La pregunta principal para mí en este caso fue, ¿por qué la API de mgo requiere un puntero a un segmento para iter.All
? Después de todo, los segmentos son tipos de referencia y para los cambios en el conjunto de datos proporcionado, no es necesario ningún puntero. Pero entonces se me ocurrió que la mayor parte del tiempo la función se agrega a la porción . Al agregar los cables a la asignación de memoria, la asignación de memoria lleva a copiar los datos antiguos a la nueva memoria, nueva memoria significa una nueva dirección que debe comunicarse al llamante.
Este comportamiento se ilustra en este ejemplo en el juego . En esencia:
// 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))
Revisa el Fragmento de abajo:
http://play.golang.org/p/xusdITxgT-
¿Por qué está pasando esto? Porque uno de mis argumentos debe ser una dirección de segmento.
Tal vez no lo dejé en claro para todos.
collection.Find(bson.M{}).All(&result)
El código anterior es por qué necesito una dirección de división.
La variable de resultado aquí es lo que necesito. Ahora usualmente puedo hacer esto
result := make([]SomeStruct, 10, 10)
Pero ahora SomeStruct es dinámico y necesito crear la división usando reflect.MakeSlice, So
result := reflect.MakeSlice(reflect.SliceOf(SomeType))
Y se enciende: el resultado debe ser una dirección de segmento.
Creo que estás siguiendo el método de la interface()
aquí, pero no estoy seguro de por qué necesitarías crear una parte posterior de un tipo a través de 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))
}
Produce:
[]*main.My 10 10