Go is a call-by-value language, meaning it will use a copy of each function arguments, if we want to update a variable we need to pass its address using a pointer. Go pointer syntax are similar as C.

Pointer basics Get address of a variable using & operator Derefence using pointer * operator

// int variable
year := 2020
// we can get address using & op
year_add := &year
fmt.Println("Val of year: ", year)
fmt.Println("Address of year: ", &year)
fmt.Println("Val of year using pointer: ", *(&year))

Function call by value

func call_by_val(year int) {
  year = year + 1
}
call_by_val(year)
// year won't be updated as it uses a copy in call_by_val function
fmt.Println("Call by val res: ", year)

Function cal by reference

func call_by_ref(year *int) {
  *year = *year + 1
}
call_by_ref(&year)
// here it will update the variable as we pass the address of the variable
fmt.Println("Call by ref res: ", year)

Method with Pointer Receiver When using pointer receiver in methods, Go compiler does implicit conversion to match the type of receiver argument with receiver parameter. To explain different use cases I will use a Point type and some methods as follows

type Point struct {
  X, Y int
}
func (p *Point) Scale(factor int) {
  p.X = p.X * factor
  p.Y = p.Y * factor
}
// pointer receiver param
func (p *Point) Print() {
  fmt.Println("X: ", p.X, ", Y: ", p.Y)
}

// receiver param of type Point, any change won't be reflected on receiver argument
func (p Point) ScaleVal(factor int) {
  p.X = p.X * factor
  p.Y = p.Y * factor
}

// value receiver param
func (p Point) PrintP() {
  fmt.Println("X: ", p.X, ", Y: ", p.Y)
}
  • Use case 1: both uses same pointer type No implicit conversion
    p := Point{X:2, Y:3}
    p_ptr := &p
    Scale(p_ptr, 2)
    // this will print the point by 2
    Print(p_ptr)
    
  • Use case 2: receiver param is of type pointer
    // receiver arg is of type Point, compiler implicitly get &p to match type with receiver param
    p.Scale(2)
    p.Print()
    
  • User case 3: receiver argument is of type pointer
    // receiver param is of type Point but receiver arg is a pointer, compiler dereference the receiver but any mutation won't be reflected on receiver arg
    p_ptr.ScaleVal(2)
    p_ptr.PrintP()