golang 反射reflect.MakeSlice 无法寻址问题
一、常见可能其实是在利用接口提取relect生成的slice值中,忘记将接口转为slice。 go语言中存储在接口的值无法寻址。
type My struct {
Name string
Id int
}
func main() {
my := &My{}
myType := reflect.TypeOf(my)
slice := reflect.MakeSlice(reflect.SliceOf(myType), 10, 10).Interface()
以下编译不通过,slice[0] (type interface{} does not support indexing
//slice[0]=&My{“Foo“”,1}
或者
//slice = slice.([]*My) //slice依然是interface{}
//slice[0]=&My{"Foo",1} //不可以
//**解决方法:必须将接口转为slice,才可以使用
p := slice.([]*My)
p[0]=&My{"Foo",1} //可以
fmt.Println(slice)
fmt.Printf("%T %d %d\n", p, len(p), cap(p))
}
二、链接上的问题,利用reflect.New()
创建指针解决
其实我觉得下面的描述更直观。此时会报panic: reflect: reflect.flag.mustBeAssignable using unaddressable value
,因为此时数组不可寻址。
func main() {
my := &My{}
myType := reflect.TypeOf(my)
arr:=reflect.Zero(reflect.ArrayOf(10,myType))
a:=&My{"Foo",1}
arr.Index(1).Set(reflect.ValueOf(a))
//panic: reflect: reflect.flag.mustBeAssignable using unaddressable value
fmt.Println("arr[1]是否可寻址",arr.Index(1).CanAddr()) //false
fmt.Println("arr[1]是否可Set",arr.Index(1).CanSet()) //false
}
利用reflect.New()
创建指针解决,变成可寻址数组:
prt:=reflect.New(arr.Type())
prt.Elem().Set(arr)
prt.Elem().Index(1).Set(reflect.ValueOf(a))
fmt.Println("arr[1]是否可以寻址",prt.Elem().Index(1).CanAddr()) //true
fmt.Println("arr[1]是否可以SET",prt.Elem().Index(1).CanSet()) //true
fmt.Println("arr:",prt.Elem().Interface()) //arr: [<nil> 0xc0000a2500 <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil>]
如有理解不对,望指出,谢谢~
为什么 reflect.MakeSlice 返回一个不可寻址的Value?
在Go中,可寻址性的一个宽松定义是,你可以获取某个东西的地址,并确保这个地址指向某个有意义的地方。如果在函数体的栈上分配了某些内容,被分配的值的地址在某个时间节点将不再可访问。因此,该值不可寻址。在大多数情况下,如果本地栈变量被返回或以其他方式提升到外部,Go会将它们移动到堆中,但在运行时不会这样做( but at runtime this is not done)。因此,CanAddr()仅在以下情况下返回true:
A value is addressable if it is an element of a slice, an element of an addressable array, a field of an addressable struct, or the result of dereferencing a pointer.
如果值是切片的元素、可寻址数组的元素、可寻址结构体的字段或解引用的指针结果,则该值是可寻址的。
声明的类型都有一个共同点:它们保证它们所持有的东西可以从任何地方访问,并且指向内存中有意义的值。由于您使用reflect.MakeSlice
创建了本地切片,所以您既没有切片元素,也没有指针,也没有任何其他提到的内容。但是,该切片的元素是可寻址的(因为该切片的内存保留在堆上)。