22

I am currently learning to program with Go language. I am having some difficulties understanding Go pointers (and my C/C++ is far away now...). In the Tour of Go #52 (http://tour.golang.org/#52) for example, I read:

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}

But if instead of

func (v *Vertex) Abs() float64 {
[...]
v := &Vertex{3, 4}

I wrote:

func (v Vertex) Abs() float64 {
[...]
v := Vertex{3, 4}

Or even:

func (v Vertex) Abs() float64 {
[...]
v := &Vertex{3, 4}

and vice-versa:

func (v *Vertex) Abs() float64 {
[...]
v := Vertex{3, 4}

I got the exact same result. Is there a difference (memory-wise, etc)?

Gökhan Barış Aker
  • 4,445
  • 5
  • 25
  • 35
merlin Gaillard
  • 233
  • 1
  • 2
  • 7
  • 2
    Hello and Welcome to Programmers. Direct implementation questions such as these are off-topic here but on topic on Stack Overflow. I will initiate a migrate. Have a pleasant day. –  Feb 26 '13 at 14:40
  • 2
    Try mutating the `v` in all the methods, and then `fmt.Println()` the original after the call, and you'll see the difference. With the `(v Vertex)` versions, you're getting a copy of the original. If it was called on a pointer, it's just dereferenced for you automatically. – the system Feb 26 '13 at 18:22
  • 1
    See also: [Method receivers ambiguity](http://stackoverflow.com/questions/14926860/method-receivers-ambiguity) – the system Feb 26 '13 at 18:26

5 Answers5

33

There are two different rules of the Go language used by your examples:

  1. It is possible to derive a method with a pointer receiver from a method with a value receiver. Thus func (v Vertex) Abs() float64 will automatically generate an additional method implementation:

    func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X+v.Y*v.Y) }
    func (v *Vertex) Abs() float64 { return Vertex.Abs(*v) }  // GENERATED METHOD
    

    The compiler will automatically find the generated method:

    v := &Vertex{3, 4}
    v.Abs()  // calls the generated method
    
  2. Go can automatically take the address of a variable. In the following example:

    func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X+v.Y*v.Y) }
    func main() {
        v := Vertex{3, 4}
        v.Abs()
    }
    

    the expression v.Abs() is equivalent to the following code:

    vp := &v
    vp.Abs()
    
  • Does doing *v generates a copy of v in memory (Vertex struct)? If No then do the change in v itself during execution of func (v Vertex) Abs() reflect outside? – MaX Sep 18 '13 at 04:24
  • 2
    A new copy of struct Vertex is created before entering `func (v Vertex) Abs()`. –  Sep 19 '13 at 07:53
  • 1
    @Atom Isn't it more correct to write in the first case, when the struct Vertex is copied before entering `func (v Vertex) Abs()`: `func (v *Vertex) Abs() float64 { return (*v).Abs() } // GENERATED METHOD`? I'm having problems understanding Vertex.Abs(*v) ... – eAbi Mar 02 '14 at 22:12
  • `(*v).Abs()` would also be correct and it is equivalent to `Vertex.Abs(*v)`. The two expressions are equivalent, they only have different syntax. –  Mar 03 '14 at 14:02
  • How do you get the pointer to a member of a struct? You've got the point to v. How do you get the pointer to a member of v? – Justin Thomas Jul 08 '14 at 17:30
13

There are differences. For example, the non-pointer receiver form forces the method to work on a copy. This way the method is not able to mutate the instance it was invoked on - it can access only the copy. Which might be ineffective in terms of e.g. time/memory performance/consumption etc.

OTOH, pointer to instances and methods with pointer receivers allow for easy instance sharing (and mutating) where desirable.

More details here.

zzzz
  • 87,403
  • 16
  • 175
  • 139
2

The difference is pass-by-referenve vs pass-by-value.

In func f(v Vertex) the argument is copied into parameter v. In func f(v *Vertex) a pointer to an existing Vertex instance is passed.

When using methods, some of the dereferencing can be done for you, so you can have a method func (v *Vertex) f() and call it without taking a pointer first: v := Vertex{...}; v.f(). This is just a grain of syntax sugar, AFAIK.

9000
  • 39,899
  • 9
  • 66
  • 104
0

There are two main differences in those examples:

func (v *Vertex) Abs()....

The receiver will be passed-by-reference for v and you would be able to call this method only on pointers:

v := Vertex{1,3}
v.Abs() // This will result in compile time error
&v.Abs() // But this will work

On the other hand

func (v Vertex) Abs() ....

You can call this method on both pointers and structs. The receiver will be passed-by-value even when you call this method on pointers.

v := Vertex{1,3}
v.Abs() // This will work, v will be copied.
&v.Abs() // This will also work, v will also be copied.

You can declare both func (v *Vertex) and func (v Vertex).

ndyakov
  • 37
  • 2
0

As the specification 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():

In your case v.Abs() is shorthand for &v.Abs() if method is addressable.

Kasinath Kottukkal
  • 2,471
  • 4
  • 22
  • 28