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

3
  • 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. Commented Feb 26, 2013 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. Commented Feb 26, 2013 at 18:22
  • 1
    See also: Method receivers ambiguity Commented Feb 26, 2013 at 18:26

5 Answers 5

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()
    
Sign up to request clarification or add additional context in comments.

5 Comments

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?
A new copy of struct Vertex is created before entering func (v Vertex) Abs().
@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) ...
(*v).Abs() would also be correct and it is equivalent to Vertex.Abs(*v). The two expressions are equivalent, they only have different syntax.
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?
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.

Comments

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.

Comments

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

Comments

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.

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.