接口赋值是否会复制任何内容?

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

Does assigning value to interface copy anything?

问题

我一直在努力理解Go语言中接口的概念。阅读了这个这个对我帮助很大。

唯一让我感到不舒服的是语法。请看下面的示例

package main

import "fmt"

type Interface interface {
    String() string
}

type Implementation int

func (v Implementation) String() string {
    return fmt.Sprintf("Hello %d", v)
}

func main() {
    var i Interface
    impl := Implementation(42)
    i = impl
    fmt.Println(i.String())
}

我的问题是i = impl这一行。根据接口实例实际上持有对实际数据的指针引用的事实,我更自然地会写成i = &impl。通常情况下,当不使用&时,对非指针进行赋值会在内存中进行完整的数据复制,但是在赋值给接口时,似乎会绕过这一点,而是**简单地(在幕后)将指针赋值给接口值。我是对的吗?**也就是说,int(42)的数据不会在内存中被复制吗?

英文:

I've been trying to wrap my head around the concept of interfaces in Go. Reading this and this helped a lot.

The only thing that makes me uncomfortable is the syntax. Have a look at the example below:

package main

import "fmt"

type Interface interface {
    String() string
}

type Implementation int

func (v Implementation) String() string {
	return fmt.Sprintf("Hello %d", v)
}

func main() {
	var i Interface
	impl := Implementation(42)
	i = impl
	fmt.Println(i.String())
}

My issue is with i = impl. Based on the fact that an interface instance actually holds a pointer reference to the actual data, it would feel more natural for me to do i = &impl. Usually assignment of non-pointer when not using & will make a full memory copy of the data, but when assigning to interfaces this seem to side-step this and instead simply (behind the scenes) assign the pointer to the interface value. Am I right? That is, the data for the int(42) will not be copied in memory?

答案1

得分: 7

int(42)的数据将会被复制。尝试运行以下代码:

func main() {
    var i Interface
    impl := Implementation(42)
    i = impl
    fmt.Println(i.String())
    impl = Implementation(91)
    fmt.Println(i.String())
}

你会发现第二个i.String()仍然显示为42。Go语言中比较棘手的一个方面是方法接收者也可以是指针。

func (v *Implementation) String() string {
    return fmt.Sprintf("Hello %d", *v)
}

// ...
i = &impl

如果你希望接口持有对impl原始值的指针,那么你需要这样做。从底层来看,接口是一个结构体,它要么持有指向某些数据的指针,要么持有数据本身(以及我们可以忽略的一些类型元数据)。如果数据本身的大小小于或等于一个机器字(machine word),则存储数据本身,无论它是指针、结构体还是其他值。

否则,它将持有指向某些数据的指针,但这里有一个棘手的部分:如果实现接口的类型是一个结构体,那么指针将指向结构体的副本,而不是分配给接口变量本身的结构体。或者至少从语义上来说,用户可以这样认为,优化可能会在两者分离之前(例如,在调用String或重新分配impl之前)不复制该值。

简而言之:将值分配给接口可以从语义上看作是实现接口的数据的副本。如果这是一个指向类型的指针,它会复制指针;如果是一个大的结构体,它会复制大的结构体。接口在底层使用指针的细节是为了垃圾回收和确保堆栈按可预测的量扩展。就开发人员而言,应该将它们视为分配给实现类型的特定实例的语义副本。

英文:

The data for int(42) will be copied. Try this code:

func main() {
    var i Interface
    impl := Implementation(42)
    i = impl
    fmt.Println(i.String())
    impl = Implementation(91)
    fmt.Println(i.String())
}

(Playground link)

You'll find that the second i.String() still shows 42. Perhaps one of the trickier aspects of Go is that method receivers can be pointers as well.

func (v *Implementation) String() string {
    return fmt.Sprintf("Hello %d", *v)
}

// ...
i = &impl

Is what you want if you want the interface to hold a pointer to the original value of impl. "Under the hood" an interface is a struct that either holds a pointer to some data, or the data itself (and some type metadata that we can ignore for our purposes). The data itself is stored if its size is less than or equal to one machine word -- whether it be a pointer, struct, or other value.

Otherwise it will be a pointer to some data, but here's the tricky part: if the type implementing the interface is a struct the pointer will be to a copy of the struct, not the struct assigned to the interface variable itself. Or at least semantically the user can think of it as such, optimizations may allow the value to not be copied until the two diverge (e.g. until you call String or reassign impl).

In short: assigning to an interface can semantically be thought of as a copy of the data that implements the interface. If this is a pointer to a type, it copies the pointer, if it's a big struct, it copies the big struct. The particulars of interfaces using pointers under the hood are for reasons of garbage collection and making sure the stack expands by predictable amounts. As far as the developer is concerned, they should be thought of as semantic copies of the specific instance of the implementing type assigned.

huangapple
  • 本文由 发表于 2014年1月2日 08:39:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/20874798.html
匿名

发表评论

匿名网友

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

确定