Go使用指针接收器调用函数的语法

时间:2012-03-31 17:27:00

标签: pointers syntax go

在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)
    }

3 个答案:

答案 0 :(得分:9)

@jnml提供了完美的doc规范说明,但我想添加一个基于你的代码示例。我认为你应该更少关注“为什么有两种方法可以做同样的事情”,更多的是关于何时使用一种方法与另一种方式。具有指针作为接收器的方法具有修改该接收器的值的方法,而具有作为接收器的值的方法不能。这是因为方法接收接收器的副本。获得指针的副本后,仍可以修改其值。当您收到值的副本时,您在该方法中所做的更改只会更改副本,而不会更改原始文件:

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

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

答案 1 :(得分:4)

规格says

  

相应指针类型* T的方法集是具有receiver * T或T的所有方法的集合(也就是说,它还包含T的方法集)。

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

  

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

将上述两个事物放在一起,你会得到你所看到的行为。

答案 2 :(得分:2)

一个简短的解释是幕后的Go编译器会自动转换:

m1.reset()
m2.reset()

成:

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