9

In Go, if I define a function with pointer as the receiver, shouldn't it allow call to the function from a pointer only? Why is it ok to call this function from the value itself and have the same effect.

For example, in following program: m1.reset() & m2.reset() have the same effect. Even though m1 is a value and m2 is a pointer.

I'm a bit confused as there are two ways of doing the same thing and am not sure which one to follow. Though most of the code follows the convention of calling the function using pointer field. Am I missing something?

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)
    }
1
  • Also you can add, Shouldn't it dereference the receiver parameter passed, m *MyStruct, to access its field X inside the definition of the function reset(), like (*m).X = 0. But this is also a convenience shorthand or syntax sugar. Commented Jun 4, 2015 at 9:39

3 Answers 3

13

@jnml offers the perfect doc spec explanation, but I wanted to add a code example based on yours. I think your focus should be less about "Why are there two ways to do the same thing" and more about when to use one vs the other. A method which has a pointer as the receiver has the ability to modify the values of that receiver, while a method which has a value as the receiver cannot. This is because the methods receive a copy of the receiver. When you get a copy of a pointer, you can still modify its value. When you receive a copy of the value, changes you make in that method only change the copy, and never the original:

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

Output

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

You can see that the way you access these variables isn't really the issue. Its more about what you can do with them inside of the method, and, how they are passed as arguments to other functions or methods (being copied).

Sign up to request clarification or add additional context in comments.

Comments

8

Specs says:

The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T).

The next piece of necessary info about method calls says:

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m().

Put the two above things together and you get the behavior you see.

1 Comment

Also one can add, "Shouldn't it dereference the receiver parameter passed, m *MyStruct, to access its field X inside the definition of the function reset(), like (*m).X = 0 ?". But this is also a convenience shorthand or syntax sugar.
4

A short explanation is that the Go compiler behind the scenes automatically converts:

m1.reset()
m2.reset()

into:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.