为什么我不能使用反射来获取切片的地址?

huangapple go评论85阅读模式
英文:

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)
}

huangapple
  • 本文由 发表于 2014年11月6日 02:56:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/26764907.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定