Application 2018-11-13

Go Pointers Explained: When to Use Them and Common Pitfalls

Understand Go pointers clearly—when to use pointer receivers vs value receivers, how pointers affect memory and performance, and the most common mistakes to avoid.

Read in: ja
Go Pointers Explained: When to Use Them and Common Pitfalls

Overview

Golang Pointers Basics discussed pointers from the perspective of reference passing and value passing. However, I became confused while handling pointers, so I will summarize the overview of pointers.

Pointer Type

Defining Pointer Type

var s *string // Variable s is a pointer of type string. The type is *string

Address Operator and Indirection Operator

Address Operator &

Indirection Operator *

Pointer Type Variable

package main

import "fmt"

func main() {
  /**
   * Definition of pointer type
   */
  var pointer *string // Pointer variable of type *string
  var s string
  s = "Hello World"

  /*
   * Handling pointers with address operator
   */
  fmt.Printf("%T\n", pointer) // *string
  fmt.Printf("%v\n", pointer) // <nil>  ※ The value of an uninitialized pointer type is nil

  fmt.Printf("%T\n", &pointer) // **string  ※ Since pointer is a pointer variable of type *string, &pointer generates a pointer of *string
  fmt.Printf("%v\n", &pointer) // 0xc00000c028  ※ Obtain the address of the pointer using &

  fmt.Printf("%T\n", s) // string
  fmt.Printf("%v\n", s) // Hello World

  pointer = &s // ※ Generate a pointer type from s and assign the address to the *string pointer variable (pointer is already defined as a pointer variable of type *string, so use & to generate the pointer type of variable s and assign it).
  fmt.Printf("%T\n", pointer) // *string  ※ pointer remains a pointer of type *string
  fmt.Printf("%v\n", pointer) // 0xc00000e1e0
  fmt.Printf("%v\n", *pointer) // Hello World  ※ Reference the value from pointer (pointer variable of type *string)
  fmt.Printf("%T\n", s) // string
  fmt.Printf("%v\n", s) // Hello World
  fmt.Printf("%T\n", &s) // *string
  fmt.Printf("%v\n", &s) // 0xc00000e1e0
  fmt.Printf("%v\n", *s) // invalid indirect of s (type string)  ※ Error because s is not a pointer type variable. Indirect means indirect.

  *pointer = "New World" // ※ *pointer is a variable, so it can be assigned. Pointer variable definition uses *.
  fmt.Printf("%T\n", *pointer) // string  ※ *pointer references the value from the pointer variable
  fmt.Printf("%v\n", *pointer) // New World
}

Structs and Pointers

Review of Structs

package main

import "fmt"

type Human struct {
  Name string
  Age int
}

func main() {
  var h Human

  fmt.Printf("%v\n", h.Name) // ""  ※ Zero value of string
  fmt.Printf("%v\n", h.Age) // 0  ※ Zero value of int

  h = Human{
    Name: "Tom",
    Age: 20,
  }

  fmt.Printf("%v\n", h) // {Tom 20}
}

Structs and Pointers

Initializing Structs with Composite Literals

package main

import "fmt"

type Human struct {
  Name string
  Age int
}

func main() {
  var h *Human // Definition of pointer type variable

  h = &Human{ // ※ Variable h is of type *Human, so use & to assign a pointer type from struct Human.
    Name: "John",
    Age: 20,
  }

  fmt.Printf("%T\n", h) // *main.Human  ※ Generating a pointer of Human with &Human
  fmt.Printf("%v\n", h) // &{John 20}  ※ Pointer of type *main.Human
  fmt.Printf("%v\n", *h) // {John 20}  ※ Extracting the value

  h.Name = "Tom"
  h.Age = 40

  fmt.Printf("%T\n", h) // *main.Human
  fmt.Printf("%v\n", h) // &{Tom 40}
  fmt.Printf("%v\n", *h) // {John 40}  ※ Extracting the value
}

Initializing Structs with new

package main

import "fmt"

type Human struct {
  Name string
  Age int
}

func main() {
  h := new(Human)

  h.Name = "John"
  h.Age = 20

  fmt.Printf("%T\n", h) // *main.Human
  fmt.Printf("%v\n", h) // &{John 20}

  h.Name = "Tom"
  h.Age = 40
  fmt.Printf("%T\n", h) // *main.Human
  fmt.Printf("%v\n", h) // &{Tom 40}
  fmt.Printf("%v\n", *h) // {Tom 40}  ※ Extracted the value
}

Receivers and Pointers

Value Receiver

package main

import "fmt"

type Human struct {
  Name string
  Age int
}

func (h Human) say(msg string) {
  fmt.Printf("%v(%v) said %v\n", h.Name, h.Age, msg)
}

func main() {
  var h Human // Defining variable h of type Human

  fmt.Printf("%T\n", h) // main.Human
  fmt.Printf("%v\n", h) // { 0}  ※ Zero values of Name and Age

  h = Human{
    Name: "Taro",
    Age: 20,
  }

  fmt.Printf("%T\n", h) // main.Human
  fmt.Printf("%v\n", h) // {Taro 20}

  h.say("Hello") // Taro(20) said Hello
}

Pointer Receiver

package main

import "fmt"

type Human struct {
  Name string
  Age int
}

// Pointer receiver
func (h *Human) say(msg string) {
  fmt.Printf("%v(%v) said %v\n", h.Name, h.Age, msg)
}

func main() {
  var h *Human // ※ Definition of pointer type

  fmt.Printf("%T\n", h) // *main.Human
  fmt.Printf("%v\n", h) // <nil> ※ Zero value of pointer type

  h = &Human{
    Name: "Taro",
    Age: 20,
  }

  fmt.Printf("%T\n", h) // *main.Human ※ Pointer of struct &Human assigned to h
  fmt.Printf("%v\n", h) // &{Taro 20}

  h.say("Hello") // Taro(20) said Hello
}

Difference Between Value Receiver and Pointer Receiver

package main

import "fmt"

type Human struct {
  Name string
  Age int
}

func (h Human) setDataForValue(name string, age int) {
  h.Name = name
  h.Age = age
}

func (h *Human) setDataForPointer(name string, age int) {
  h.Name = name
  h.Age = age
}

func main() {
  // Calling value function
  var hForValue Human

  hForValue = Human{
    Name: "Taro",
    Age: 20,
  }

  hForValue.setDataForValue("Jiro", 40)

  fmt.Printf("Name: %v\n Age: %v\n", hForValue.Name, hForValue.Age) // Name: Taro Age: 20

  // Calling pointer function
  var hForPointer *Human

  hForPointer = &Human{
    Name: "Taro",
    Age: 20,
  }

  hForPointer.setDataForPointer("Jiro", 40)

  fmt.Printf("Name: %v\n Age: %v\n", hForPointer.Name, hForPointer.Age) // Name: Jiro Age: 40
}

When you want to change the values of struct fields within a function, use a pointer receiver. For reference types like map and chan, a value receiver is also acceptable. However, when strictly considering performance, this distinction may not hold.

When Confused About Pointers

Recall the definition of pointer types and the roles of operators described in the first half.

References

Tags: Golang
Share: 𝕏 Post Facebook Hatena
✏️ View source / Discuss on GitHub
☕ Support

If you enjoy this blog, consider supporting it. Every bit helps keep it running!


Related Articles