英文:
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论