我一直试图围绕Go中的接口概念。阅读this和this有很多帮助。
唯一让我感到不舒服的是语法。请看下面的the example:
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)
的数据不会被复制到内存中?
答案 0 :(得分:4)
将复制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
原始值的指针,那么您想要的是什么。 “引擎盖下”接口是一个结构,它可以保存指向某些数据的指针,也可以保存数据本身(以及我们可以忽略的某些类型元数据)。如果数据的大小小于或等于一个机器字,则存储数据本身 - 无论是指针,结构还是其他值。
否则它将是指向某些数据的指针,但这里是棘手的部分:如果实现接口的类型是结构,则指针将指向结构的副本,而不是指定的结构到接口变量本身。或者至少在语义上用户可以这样认为,优化可能允许在两个分歧之前不复制该值(例如,直到你致电String
或重新分配impl
)。
简而言之:分配给接口可以在语义上被视为实现接口的数据的副本。如果这是一个指向类型的指针,它会复制指针,如果它是一个大结构,它会复制大结构。在引擎盖下使用指针的接口的细节是出于垃圾收集的原因并确保堆栈以可预测的量扩展。就开发人员而言,它们应被视为已分配的实现类型的特定实例的语义副本。