Pointers in Go: Common Pitfalls and How to Avoid Them

Ambiyansyah Risyal
4 min readDec 23, 2022

--

Pointers in Go: Common Pitfalls and How to Avoid Them
Photo by Salman Saqib on Unsplash

Pointers are a fundamental concept in Go and many other programming languages. They allow you to directly manipulate the memory address of a variable, rather than just its value. While pointers can be a powerful tool, they can also be a source of bugs and errors if not used correctly. In this article, we’ll explore some common pitfalls associated with pointers in Go and how to avoid them with examples.

One common pitfall with pointers is dereferencing a nil pointer. A nil pointer is a pointer that points to a memory address of 0, which is not a valid memory address. If you try to dereference a nil pointer, it will cause a runtime panic. To avoid this pitfall, you should always check if a pointer is nil before dereferencing it. For example:

package main

import "fmt"

func main() {
var p *int

if p != nil {
fmt.Println(*p)
} else {
fmt.Println("p is nil")
}
}

In this example, we are checking if the pointer p is nil before dereferencing it. If it is nil, we print a message saying "p is nil", otherwise we print the value of *p.

Another pitfall with pointers is not releasing them properly when they are no longer needed. Go has a garbage collector that automatically releases memory that is no longer being used, but it is not always efficient at doing so. If you have pointers that are no longer needed, you should set them to nil to help the garbage collector release the memory they are pointing to. For example:

package main

import "fmt"

func main() {
var p *int
p = new(int)
*p = 42
fmt.Println(*p)
p = nil
}

In this example, we create a new pointer p and assign it the memory address of a new integer. We then set the value of the integer to 42 and print it. After we are done with the pointer, we set it to nil to help the garbage collector release the memory it is pointing to.

A third pitfall with pointers is not properly initializing them before use. If you don’t initialize a pointer, it will have an unpredictable value, which can lead to unexpected behavior and bugs. To avoid this pitfall, you should always initialize your pointers before using them. For example:

package main

import "fmt"

func main() {
var p *int
*p = 42
fmt.Println(*p)
}

In this example, we have a pointer p that is not initialized before we try to use it. This will cause a runtime panic because we are dereferencing an uninitialized pointer. To avoid this pitfall, we should initialize p before using it like this:

package main

import "fmt"

func main() {
var p *int
p = new(int)
*p = 42
fmt.Println(*p)
}

A fourth pitfall with pointers is using them without understanding how they work. Pointers can be confusing for new programmers, and it is easy to make mistakes if you don’t fully understand how they work. To avoid this pitfall, it is important to spend time learning about pointers and practicing with them before using them in your code. Here is an example of a common mistake made by inexperienced programmers:

package main

import "fmt"

func main() {
var p *int
*p = 42
fmt.Println(p)
}

In this example, we are trying to set the value of the integer pointed to by p to 42, but we are actually just setting the value of p itself to 42. To correctly set the value of the integer pointed to by p, we would need to use the dereference operator * like this:

package main

import "fmt"

func main() {
var p *int
*p = 42
fmt.Println(*p)
}

Finally, a fifth pitfall with pointers is not properly synchronizing access to them. If multiple goroutines are accessing the same pointer, you need to make sure that they are properly synchronized to avoid race conditions and data corruption. One way to do this is to use the sync package to protect access to the pointer with a mutex. Here is an example of how to do this:

package main

import (
"fmt"
"sync"
)

func main() {
var p *int
var mutex sync.Mutex

go func() {
mutex.Lock()
*p = 42
mutex.Unlock()
}()

go func() {
mutex.Lock()
fmt.Println(*p)
mutex.Unlock()
}()
}

In this example, we have two goroutines that are accessing the same pointer p. To prevent race conditions, we use a mutex to synchronize access to the pointer. The first goroutine locks the mutex before setting the value of *p to 42, and the second goroutine locks the mutex before printing the value of *p.

In conclusion, pointers can be a powerful tool in Go, but they also have the potential to cause bugs and errors if not used correctly. By understanding and avoiding the common pitfalls associated with pointers, you can write more stable and reliable code.

--

--

Ambiyansyah Risyal
Ambiyansyah Risyal

Written by Ambiyansyah Risyal

Software engineer. Lover of learning and creating. Sharing thoughts and experiences on tech and software development. Always seeking new ideas and techniques.

No responses yet