英文:
why can't I use reflection to take the address of a slice?
问题
为什么这个可以工作:
slice := make([]string, 0, 10)
sliceptr := &slice
这个也可以工作:
sliceptr := &[]string{"foo","bar","baz"}
但是这个不行:
sliceaddrval := reflect.ValueOf([]string{"foo","bar","baz"}).Addr()
它会抛出错误:reflect.Value.Addr of unaddressable value
编辑:总体来说,我想做的是,将一个未知类型的结构体,创建一个该类型的结构体切片,并返回一个指向该切片的指针(我正在使用github.com/jmoiron/modl,它要求使用指向切片的指针来填充来自SQL查询的结果)。
英文:
How come this works:
slice := make([]string, 0, 10)
sliceptr := &slice
this too:
sliceptr := &[]string{"foo","bar","baz"}
But this doesn't:
sliceaddrval := reflect.ValueOf([]string{"foo","bar","baz"}).Addr()
It panics with: reflect.Value.Addr of unaddressable value
EDIT: Overall what I'm trying to do is take a struct that is of an unknown type, make a slice of structs of that type and return a pointer to it (I'm using github.com/jmoiron/modl which requires a pointer to slice to populate with results from a SQL query).
答案1
得分: 5
reflect.Value接受一个interface{}参数,而一个interface{}类型的值不能用于修改原始值。否则,当你并没有打算传递指针时,你可能会导致代码修改struct中的数据(或者在这种情况下,修改传递的切片的长度)。因此,如果你想要获取地址,你需要在ValueOf之前这样做。
要创建一个指向切片的指针,你可以将其传递给一个会对其进行append操作的包(比如modl或Google App Engine的GetMulti),你可以使用类似下面的代码:http://play.golang.org/p/1ZXsqjrqa3
package main
import (
	"fmt"
	"reflect"
)
type row struct { i, j int }
func main() {
	aRow := row{}
	valueType := reflect.ValueOf(aRow).Type()
	slicePtrVal := reflect.New(reflect.SliceOf(valueType))
	slicePtrIface := slicePtrVal.Interface()
	getQueryResults(slicePtrIface)
	fmt.Println(slicePtrIface)
}
// 代替`modl`或者其他填充切片的函数
func getQueryResults(slicePtr interface{}) {
	sPtr := slicePtr.(*[]row)
	(*sPtr) = append((*sPtr), row{1,3}) 
}
如果你想要在reflect.Value中自己追加元素到切片,需要使用更多的reflect代码,但是听起来你正在使用的包会为你处理这部分。关于追加元素的一般信息,可以参考以下代码:http://play.golang.org/p/m3-xFYc6ON
package main
import (
	"fmt"
	"reflect"
)
type row struct { i, j int }
func main() {
	aRow := row{}
	// 创建一个指向空切片的指针
	rowType := reflect.ValueOf(aRow).Type()
	slicePtrVal := reflect.New(reflect.SliceOf(rowType))
	slicePtrIface := slicePtrVal.Interface()
	// 向其中追加一个零值行
	rowVal := reflect.Zero(rowType)
	sliceVal := reflect.Indirect(slicePtrVal)
	sliceVal.Set(reflect.Append(sliceVal, rowVal))
	fmt.Println(slicePtrIface)
}
英文:
reflect.Value takes an interface{}, and an interface{} to a value can't be used to change the original. Otherwise, you could end up with code changing data in your struct when you didn't even intend to pass it a pointer. (Or, in this case, changing the length of a slice that was passed by value.) So if you take the address you'd have to do it before the ValueOf.
To make a pointer to a slice that you can to pass to a package that will append to it (like modl or Google App Engine GetMulti), you'd  use something like http://play.golang.org/p/1ZXsqjrqa3, copied here:
package main
import (
	"fmt"
	"reflect"
)
type row struct { i, j int }
func main() {
	aRow := row{}
	valueType := reflect.ValueOf(aRow).Type()
	slicePtrVal := reflect.New(reflect.SliceOf(valueType))
	slicePtrIface := slicePtrVal.Interface()
	
	getQueryResults(slicePtrIface)
	fmt.Println(slicePtrIface)
}
// standing in for `modl` or whatever populates the slice
func getQueryResults(slicePtr interface{}) {
	sPtr := slicePtr.(*[]row)
	(*sPtr) = append((*sPtr), row{1,3}) 
}
Appending to a slice in a reflect.Value yourself takes another few lines of reflect, but it sounds like the package you're working with takes care of that part for you. For general info, code to do the append is at http://play.golang.org/p/m3-xFYc6ON and below:
package main
import (
	"fmt"
	"reflect"
)
type row struct { i, j int }
func main() {
	aRow := row{}
	// make a pointer to an empty slice
	rowType := reflect.ValueOf(aRow).Type()
	slicePtrVal := reflect.New(reflect.SliceOf(rowType))
	slicePtrIface := slicePtrVal.Interface()
	// append a zero row to it
	rowVal := reflect.Zero(rowType)
	sliceVal := reflect.Indirect(slicePtrVal)
	sliceVal.Set(reflect.Append(sliceVal, rowVal))
	
	fmt.Println(slicePtrIface)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论