【问题标题】:Go Syntax of calling a function with pointer receiver使用指针接收器调用函数的 Go 语法
【发布时间】:2012-03-31 17:26:59
【问题描述】:

在Go中,如果我定义一个带有指针的函数作为接收者,它不应该只允许从指针调用函数吗?为什么从值本身调用这个函数就可以了,效果一样。

例如,在以下程序中: m1.reset() & m2.reset() 具有相同的效果。即使 m1 是一个值,而 m2 是一个指针。

我有点困惑,因为有两种方法可以做同样的事情,我不确定该遵循哪一种。尽管大部分代码都遵循使用指针字段调用函数的约定。我错过了什么吗?

package main

    import "fmt"

    type MyStruct struct {
        X int
    }

    func (m *MyStruct) reset() {
        m.X = 0
    }

    func main() {
        m1 := MyStruct{1}
        m2 := &MyStruct{1}

        fmt.Println(m1.X)
        fmt.Println(m2.X)

        m1.reset()
        m2.reset()

        fmt.Println(m1.X)
        fmt.Println(m2.X)
    }

【问题讨论】:

  • 您还可以添加,难道不应该取消引用传递的接收器参数m *MyStruct,以访问函数reset() 定义内的字段X,如(*m).X = 0。但这也是一种方便的速记或语法糖。

标签: pointers syntax go


【解决方案1】:

@jnml 提供了完美的文档规范解释,但我想根据你的添加一个代码示例。我认为您的关注点应该更少关注“为什么有两种方法可以做同一件事”,而应该更多关注何时使用一种与另一种。具有作为接收者的指针的方法能够修改该接收者的值,而具有作为接收者的值的方法不能。这是因为方法接收到接收者的副本。当你得到一个指针的副本时,你仍然可以修改它的值。当您收到值的副本时,您在该方法中所做的更改只会更改副本,而不会更改原始值:

package main

import "fmt"

type MyStruct struct {
    X int
}

func (m *MyStruct) resetPtr() {
    m.X = 0
}

func (m MyStruct) resetValue() {
    m.X = 0
}

func main() {
    m1 := MyStruct{1}
    m2 := &MyStruct{1}

    fmt.Println("Original Values:", m1.X, m2.X)

    m1.resetPtr()
    m2.resetPtr()

    fmt.Println("After resetPtr():", m1.X, m2.X)

    m1 = MyStruct{1}
    m2 = &MyStruct{1}

    m1.resetValue()
    m2.resetValue()

    fmt.Println("After resetValue():", m1.X, m2.X)
}

输出

Original Values: 1 1
After resetPtr(): 0 0
After resetValue(): 1 1

您可以看到访问这些变量的方式并不是真正的问题。它更多地是关于您可以在方法内部使用它们做什么,以及如何将它们作为参数传递给其他函数或方法(被复制)。

【讨论】:

    【解决方案2】:

    规格says:

    对应指针类型*T的方法集是所有接收者为*T或T的方法的集合(即它也包含T的方法集)。

    关于method calls 的下一条必要信息说:

    一个方法调用x.m()是有效的,如果x(的类型)的方法集包含m并且参数列表可以分配给m的参数列表。如果 x 是可寻址的并且 &x 的方法集包含 m,则 x.m()(&x).m() 的简写。

    将以上两件事放在一起,你就会得到你所看到的行为。

    【讨论】:

    • 还可以添加,“它不应该取消引用传递的接收器参数m *MyStruct,以访问其在函数reset() 定义中的字段X,如@987654329 @?”。但这也是一种方便的速记或语法糖。
    【解决方案3】:

    简单解释一下,Go 编译器在后台自动转换:

    m1.reset()
    m2.reset()
    

    进入:

    (&m1).reset()
    m2.reset()
    

    【讨论】:

      猜你喜欢
      • 2013-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-31
      • 1970-01-01
      • 2014-04-08
      • 2017-02-10
      • 1970-01-01
      相关资源
      最近更新 更多