在Golang中为什么存储在接口中的值不可寻址(Why value stored in an interface is not addressable in Golang)

引用golang wiki( https://github.com/golang/go/wiki/MethodSets#interfaces ):

“存储在接口中的具体值不可寻址,就像地图元素不可寻址一样。”

这里解释了地图值不可寻址的问题: 为什么地图值不可寻址?

但是,关于界面并不清楚。 他们为什么不可寻址? 这是因为一些硬设计假设?

Citing the golang wiki (https://github.com/golang/go/wiki/MethodSets#interfaces):

"The concrete value stored in an interface is not addressable, in the same way, that a map element is not addressable."

The question of map values not being addressable is explained here: Why are map values not addressable?

However, it is not clear regarding the interface. Why are they not addressable? Is this because of some hard design assumption?

最满意答案

为什么接口中存储的非指针值不可寻址? 这是一个很好的问题,答案解释了为什么包含非​​指针值的接口不能成为具有指针接收器的方法的接收器,导致出现可怕的错误:

<type> does not implement <interface> (<name> method has pointer receiver)

TL;博士

存储在接口中的非指针值不可寻址以维持类型完整性。 例如,当接口中存储不同类型B的值时,指向A的指向接口中类型A的值的指针将失效。

由于存储在接口中的非指针值不可寻址,因此编译器无法将其地址传递给具有指针接收器的方法。

长答案

我在网上看到的答案没有多大意义。 例如, 这篇文章说:

原因是接口中的值位于隐藏的内存位置,因此编译器无法为您自动获取指向该内存的指针(在Go用语中,这称为“不可寻址”)。

存储在接口中的值确实是不可寻址的,但据我所知,并不是因为它存储在“隐藏的存储位置”中。

另一个常见的答案是:

当创建接口值时,将复制接口中包装的值。 因此不可能采用它的地址,即使你这样做,使用指向接口值的指针也会产生意想不到的效果(即无法更改原始复制值)。

这是没有意义的,因为指向复制到接口的值的指针与指向复制到具体类型的值的指针没有区别; 在这两种情况下,您都无法通过指向副本的指针更改原始复制值。

那么,为什么在接口中不存储值是可寻址的呢? 如果可以解决的话,答案就在于后续的影响。

假设您有一个接口I和两个类型AB ,它们满足该接口:

type I interface{} type A int type B string

创建一个A并将其存储在I

func main() { var a A = 5 var i I = a fmt.Printf("i is of type %T\n", i)

假设我们可以获取存储在界面中的值的地址:

var aPtr *A aPtr = &(i.(A)) // not allowed, but if it were...

现在创建一个B并将其存储在i

var b B = "hello" i = b fmt.Printf("i is of type %T, aPtr is of type %T\n", i, aPtr) }

这是输出:

i is of type main.A i is of type main.B, aPtr is of type *main.A

B放入i之后aPtr指向什么? aPtr被声明为指向A ,但t现在包含B ,并且aPtr不再是指向A的有效指针。

但是,这是允许的:

var aPtr *A var a2 A = i.(A) aPtr = &a2

因为第二行在i。(A)中复制了值,而aPtr没有指向i。(A)

那么,为什么包含非​​指针值的接口不能成为具有指针接收器的方法的接收者呢? 由于存储在接口中的非指针值不可寻址,因此编译器无法将其地址传递给具有指针接收器的方法。

Why isn't a non-pointer value stored in an interface addressable? This is an excellent question, and the answer explains why an interface containing a non-pointer value can't be the receiver for a method with a pointer receiver, leading to the dreaded error:

<type> does not implement <interface> (<name> method has pointer receiver)

tl;dr

A non-pointer value stored in an interface isn't addressable to maintain type integrity. For example, a pointer to A, which points to a value of type A in an interface, would be invalidated when a value of a different type B is subsequently stored in the interface.

Because a non-pointer value stored in an interface isn't addressable, the compiler can't pass its address to a method with a pointer receiver.

Long answer

The answers I've seen online don't make much sense. For instance, this article says:

The reason is that the value in an interface is in a hidden memory location, and so the compiler can’t automatically get a pointer to that memory for you (in Go parlance, this is known as being “not addressable”).

It's true that the value stored in an interface is not addressable, but as far as I can see it's not because its stored in "a hidden memory location".

Another common answer is:

When an interface value is created, the value that is wrapped in the interface is copied. It is therefore not possible to take its address, and even if you did, using a pointer to the interface value would have unexpected effects (ie. unable to alter the original copied value).

This makes no sense, since a pointer to a value copied into an interface would be no different than a pointer to a value copied into a concrete type; in both cases you can't alter the original copied value through the pointer to the copy.

So why isn't a value stored in an interface addressable? The answer lies in the follow-on implications if it were addressable.

Let's say you have an interface, I, and two types, A and B, which satisfy that interface:

type I interface{} type A int type B string

Create an A and store it in an I:

func main() { var a A = 5 var i I = a fmt.Printf("i is of type %T\n", i)

Let's pretend we could take the address of a value stored in an interface:

var aPtr *A aPtr = &(i.(A)) // not allowed, but if it were...

Now create a B and store it in i:

var b B = "hello" i = b fmt.Printf("i is of type %T, aPtr is of type %T\n", i, aPtr) }

Here's the output:

i is of type main.A i is of type main.B, aPtr is of type *main.A

After putting a B into i, what is aPtr pointing to? aPtr was declared as pointing to an A, but t now contains a B, and aPtr is no longer a valid pointer to A.

This, however is permitted:

var aPtr *A var a2 A = i.(A) aPtr = &a2

Because the second line makes a copy of the value in i.(A), and aPtr does not point to i.(A).

So, why can't an interface containing a non-pointer value be the receiver for a method with a pointer receiver? Because a non-pointer value stored in an interface isn't addressable, so the compiler can't pass its address to a method with a pointer receiver.

更多推荐