为什么 golang 的 reflect.MakeSlice 返回不可寻址的 Value?

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

Why golang reflect.MakeSlice returns un-addressable Value

问题

请查看下面的代码片段:

检查下面的代码片段

[http://play.golang.org/p/xusdITxgT-][1]

为什么会发生这种情况因为我的一个参数必须是切片地址

也许我没有清楚地向每个人解释清楚

collection.Find(bson.M{}).All(&result)

上面的代码是我为什么需要一个切片地址

这里的result变量是我需要的通常我可以这样做

result := make([]SomeStruct, 10, 10)

但现在SomeStruct是动态的我需要使用reflect.MakeSlice来创建切片所以

result := reflect.MakeSlice(reflect.SliceOf(SomeType))

然后它报错result必须是切片地址

  [1]: http://play.golang.org/p/xusdITxgT-

请注意,这只是翻译的结果,不包含任何其他内容。

英文:

check the Snippet below:

http://play.golang.org/p/xusdITxgT-

Why is this happening? Because one of my argument must be a slice address.

Maybe I did not made it clear for everyone.

collection.Find(bson.M{}).All(&result)

The above code is why I need a slice address.

the result variable here is what I need. Now usually I can do this

result := make([]SomeStruct, 10, 10)

But now the SomeStruct is dynamic and I need to create the slice by using reflect.MakeSlice, So

result := reflect.MakeSlice(reflect.SliceOf(SomeType))

And it errors on : result must be a slice address.

答案1

得分: 62

如何使用反射获取切片的指针

最简单的解决方案可能是使用reflect.New()来创建指针(完整示例请参见此处):

my := &My{}

// 首先创建一个切片
myType := reflect.TypeOf(my)
slice := reflect.MakeSlice(reflect.SliceOf(myType), 10, 10)

// 创建一个指向切片值的指针,并将其设置为切片
x := reflect.New(slice.Type())
x.Elem().Set(slice)

collection.Find(bson.M{}).All(x.Interface())

请注意,其他答案中也指出了x.Interface()的作用。这样可以确保将All()方法传递给reflect.Value而不是x的实际值。

为什么reflect.MakeSlice返回一个不可寻址的值?

在Go语言中,可寻址性的宽松定义是指您可以获取某个对象的地址,并确保该地址指向有意义的位置。如果您在函数体内在堆栈上分配了某个值,那么在某个时间点上,该分配值的地址将不再可访问。因此,该值是不可寻址的。在大多数情况下,如果返回或以其他方式提升到外部的局部堆栈变量,Go会将其移动到堆上,但在运行时不会执行此操作。因此,CanAddr()仅在以下情况下返回true

如果一个值是切片的元素、可寻址数组的元素、可寻址结构体的字段或者解引用指针的结果,则该值是可寻址的。

上述类型都有一个共同点:它们保证它们所持有的内容将从任何地方都可以访问,并且指向内存中的有意义的值。您既没有切片的元素,也没有指针,也没有其他提到的东西,因为您使用reflect.MakeSlice创建了一个局部切片。但是,该切片的元素是可寻址的(因为切片的内存位于堆上)。

为什么需要切片的指针?

在这种情况下,对我来说最主要的问题是,为什么mgo的API要求对iter.All使用切片的指针?毕竟,切片是引用类型,对于提供的数据集的更改,不需要指针。但是后来我想到,大多数情况下,该函数会向切片追加数据。追加操作会导致内存分配,内存分配会导致将旧数据复制到新内存中,新内存意味着新的地址需要通知调用者。

这种行为在此示例中有所说明。简而言之:

// 可行。使用切片的可用存储空间。
resultv.Index(1).Set(a)

// 执行但更改会丢失:
// reflect.Append(resultv, a)

// 不可行:reflect.Value.Set使用不可寻址的值
// resultv.Set(reflect.Append(resultv, a))
英文:

How to get a pointer to a slice using reflection

The simplest solution is probably to use reflect.New() to create the pointer (full example on play):

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 the x.Interface() that was pointed out by other answers as well. This prevents that instead of the reflect.Value the actual value of x is passed to All().

Why does reflect.MakeSlice return an un-addressable Value?

A loose definition of addressability in Go is that you can take the address of something and are guaranteed that this address points to somewhere meaningful. If you allocate something on the stack in the body of a function, the address of the allocated value will, at some point in time, not be accessible anymore. Therefore, this value is not addressable. In most cases, Go moves local stack variables to the heap if they are returned or otherwise promoted to the outside, but at runtime this is not done. Therefore, CanAddr() returns only true when:

> 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.

The stated types all have one thing in common: they guarantee that what they hold will be accessible from everywhere and point to a meaningful value in memory. You have neither a slice element, nor a pointer, nor any of the other mentioned things since you created a local slice using reflect.MakeSlice. The elements of said slice would be addressable though (since the slice's memory resides on the heap).

Why a pointer to a slice?

The main question for me in this case was, why does the API of mgo require a pointer to a slice for iter.All? After all, slices are reference types and for changes in the provided data set, no pointer is necessary. But then it occurred to me that most of the time the function appends to the slice. Appending leads to memory allocation, memory allocation leads to copying the old data to new memory, new memory means a new address which needs to be communicated to the caller.

This behaviour is illustrated in this example on play. In essence:

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

答案2

得分: 3

我认为你在这里需要使用interface()方法,但我不确定为什么你需要通过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))
}

输出结果为:

[]*main.My 10 10

Playground

英文:

I think you're after the interface() Method here, but I'm not sure why you would need to create a slice back of a type via 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))
}

Produces:

<pre>
[]*main.My 10 10
</pre>

Playground

huangapple
  • 本文由 发表于 2014年8月19日 21:26:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/25384640.html
匿名

发表评论

匿名网友

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

确定