Pointers in Go
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()