pointers - 为什么Go允许将接口(interface)变量分配给未实现值接收者的值?

标签 pointers go interface go-interface

很抱歉,这个问题标题很混乱,但我有点不明白。在下面的代码中,当我们试图将x分配给p时会出现错误,因为x需要p来实现M(),而它没有,因为M()有指针接收器。

type Person struct {
    Name string
    Age int
}

func (p *Person) M() {
}

type I interface {
    M()
}

func main() {
    var x I
    var p := Person{}
    x = p              // Error, M() has pointer receiver
}

这对我来说是有意义的。我不明白的是,在下面的示例中将x分配给&p是多么令人愉快。在本例中,M()有一个值接收器,而不是指针接收器,但它仍然可以正常工作。
type Person struct {
    Name string
    Age int
}

func (p Person) M() {
}

type I interface {
    M()
}

func main() {
    var x I
    var p := Person{}
    x = &p             // No error
}

这对我来说毫无意义。一个方法要么实现一个值接收器,要么实现一个指针接收器,所以如果它不允许您将一个接口变量分配给一个只实现指针接收器的类型的值,那么为什么它允许您将它分配给一个指针,而该方法只被定义为一个值接收器?
最后,您可以在go中执行以下操作而不出现问题,这进一步混淆了这一点:
func (p *Person) rename(string newName) {
    p.Name = newName
}

func main() {
    p := Person{Name: "Old name"}
    fmt.Println(p.Name)
    p.rename("New name")
    fmt.Println(p.Name) // Name has changed, despite p not being pointer
}

在go-tour中,它说这样的调用是显式转换的(例如p.rename("New name")隐式转换为(&p).rename("New name")并且指针在需要时变为值)。
这看起来很矛盾。我错了吗?

最佳答案

我想出来了。简而言之,我搞砸了两个概念。我有很多困惑,所以让我们把它分解,从我关于自动指针解引用的最后一点开始。
我遇到的主要问题是编译器似乎很乐意为您应用&运算符。问题是,有时这是有意义的,有时却没有意义(见下文)。

p := Person{"Old"}
var iface I = p
p.rename("New") // Can safely be rewritten to (&p).rename("New")
iface.rename("New") // Cannot rewrite (&iface != &p)

这就是为什么如果只有指向某个值的指针满足该接口,则不允许为该接口分配值是有意义的。一旦原始变量被分配给接口,您就不能简单地猛拉它的地址,但是当它是实际类型的变量时,您可以这样做。现在,这仍然让我困惑:
p := Person{"Name"}
var iface I = &p    // No error

我从中得到的大部分是…去就是这样。Thispost描述方法集。我被“可寻址”这个词绊倒了。为了解释我的困惑,我的印象是编译器在存储指针但需要值时正在执行以下插入操作(这没有意义):
p := &Person{"Name"}
var iface I = p
p.rename()  /* becomes */ (*p).rename()
iface.rename() /* becomes */ (*iface).rename()

从编译器的角度来看,执行方法调用是一个多步骤的过程;简单地将*放在变量名之前,可能会让您觉得有点麻烦,但实际上还有更多的变量需要从内存中复制并推送到堆栈上,等等。自己取消对变量的引用不会对编译器产生任何影响。在这两种情况下,它都需要转到那个位置并复制那里的内容。从go的角度来看,实际编写*只是告诉编译器做它已经做的事情,因为值接收器方法需要一个值,所以编译器需要生成一个值。

关于pointers - 为什么Go允许将接口(interface)变量分配给未实现值接收者的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54819892/

相关文章:

C 空指针类型转换

oop - 去 "polymorphism"

go - Go 中的死锁,两个例程拆分工作

c++ - 在 CoCreateInstance 之前调用 QueryInterface?

string - Go 中对字符串字面量的引用

python - 如何将使用 ctypes 调用的 C 函数返回的指针转换为 numpy 数组?

interface - 在用户空间中获取 Linux 接口(interface)流量 64 位统计信息

c# - C# 接口(interface)的非公共(public)成员

c - 为什么 scanf() 不将 array 和 &array 视为相同的?

amazon-web-services - 使用Golang上传时下载S3图片而不是显示