LahiruSenevirathne
LahiruSenevirathne

Reputation: 67

Go method on value and method on pointers whats the difference?

I want to know whats the difference of having methods on pointer and method on values. How both the methods work on standard structure instance as well as structure pointer.

Upvotes: 5

Views: 1463

Answers (2)

Eric
Eric

Reputation: 24866


Define receiver as value

Format:

func (r T) Xxx() {}

Could call by either a value or a pointer.

When call with pointer, the value will be passed automatically, (it actually use * to get value of the caller, and pass it).


Define receiver as pointer

Format:

func (r *T) Xxx() {}

In principle, should invoke with pointer only, but that's not necessary.

Because when call with value, instead of pointer, compiler will take care of it, when possible:

  • If the value is addressable, (which is true for most data type in go).
    Then compiler will take the address (via &), and pass it automatically.
    This enable to call a pointer method with value directly, (this is pretty common in go I guess, and it makes programmer's life easier).
  • If the value is not addressable, (which is rare, but exists).
    Then need to pass the address explicitly, otherwise would get error when compile.
    e.g map's element is not addressable.

Tips

  • Pointer caller is preferred, when define a method, if proper.
    Reasons:

    • It could modify the caller.
    • It's more lightweight for a complex caller.
  • What is passed to method, depends on the method signature, not how you call it (this is similar as with param).

    • When declare caller as pointer (r *T), it pass pointer.
    • When declare caller as value (r T), it pass a copy of original caller.
  • T itself can't be pointer.

Code

And, here is a go code that I wrote when learning this feature.

Its 2 functions called in main() tests the 2 features respectively.

method_learn.go:

// method - test
package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    x float64
    y float64
}

// abs, with pointer caller,
func (v *Vertex) AbsPointer() float64 {
    return math.Sqrt(v.x*v.x + v.y*v.y)
}

// scale, with pointer caller,
func (v *Vertex) ScalePointer(f float64) *Vertex {
    v.x = v.x * f
    v.y = v.y * f

    return v
}

// abs, with value caller,
func (v Vertex) AbsValue() float64 {
    return math.Sqrt(v.x*v.x + v.y*v.y)
}

// test - method with pointer caller,
func pointerCallerLearn() {
    vt := Vertex{3, 4}
    fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n", vt, vt.AbsPointer(), "pointer", "value")        // call pointer method, with value,
    fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n\n", vt, (&vt).AbsPointer(), "pointer", "pointer") // call pointer method, with pointer,

    // scala, change original caller,
    fmt.Printf("%v scale by 10 is: %v (Call %s method, with %s)\n", vt, vt.ScalePointer(10), "pointer", "value")      // call pointer method, with value,
    fmt.Printf("%v scale by 10 is: %v (Call %s method, with %s)\n", vt, (&vt).ScalePointer(10), "pointer", "pointer") // call pointer method, with pointer,
}

// test - method with value caller,
func valueCallerLearn() {
    vt := Vertex{3, 4}
    fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n", vt, (&vt).AbsValue(), "value", "pointer") // call value method, with pointer,
    fmt.Printf("Abs of %v is %v. (Call %s method, with %s)\n", vt, vt.AbsValue(), "value", "value")      // call value method, with value,
}

func main() {
    // pointerCallerLearn()
    valueCallerLearn()
}

Just modify main(), and run via go run method_test.go, then check the output to see how it works.

Upvotes: 6

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230276

The big difference between them is that value receivers are copied*. So if you want to mutate your receiver, you have to use pointer. Observe:

package main

import (
    "fmt"
)

type Person struct {
  Age int
}

func (p Person) GrowUp1() {
  p.Age++
}

func (p *Person) GrowUp2() {
  p.Age++
}

func main() {
  p := Person{Age: 20}
  fmt.Println(p)

  p.GrowUp1()
  fmt.Println(p)


  p.GrowUp2()
  fmt.Println(p)
}
// {20}
// {20}
// {21}

* Pointers are copied too, naturally. But since they're pointers, a copy of a pointer still points to the same object.

Upvotes: 2

Related Questions