DEV Community

Cover image for Understanding Pointers in Go (Especially If You’re Coming from JavaScript)
Promise Ihunna
Promise Ihunna

Posted on

Understanding Pointers in Go (Especially If You’re Coming from JavaScript)

If you are coming from JavaScript or Typescript, pointers in Go can seem overwhelming or unnecessary at first. Not because they are hard, but because it's not obvious why they matter. In JavaScript, you don't think about memory management. You just pass objects around, and voilà, everything works. So when you start seeing * and & everywhere in Go, it can start to feel a bit extra and intimidating. This was my experience too. I had a good understanding of what pointers are, but I never really got why I should care.

So in this article, I’ll walk through what pointers are, how they work, and when they actually make sense to use, in a simple and easy-to-follow way.

Let’s start with a simple struct

type Person struct {
    Id   string `json:"id"`
    Name string `json:"name"`
    Age  int  `json:"age"`
}

var persons = []Person{
    {Id:"5f919d2c-3536-4c44-8f45-ab420e089827", Name:"Promise", Age:25},
    {Id:"dcb70a59-2eb2-4bd4-a1e9-d7fcc089bfeb", Name:"Ben", Age:29},
    {Id:"f73dd3c8-5cf4-4075-b65e-1b19991fdb4f", Name:"Korede", Age:18}
}
Enter fullscreen mode Exit fullscreen mode

We'll use this as our example.

What is a pointer?
A pointer is just a variable that stores the memory address of another variable. Instead of holding a value itself, it holds the memory address of another variable.

var x int = 21
p := &x
Enter fullscreen mode Exit fullscreen mode

&x returns the address of x.

So now p is pointing to where x is stored.

To get the value of x back:

fmt.Println(*p) // 21
Enter fullscreen mode Exit fullscreen mode

*p means “go to that address and give me the value”.

Applying this to our struct

p := &persons
Enter fullscreen mode Exit fullscreen mode

Now p is pointing to the persons slice in memory

If you do:

*p
Enter fullscreen mode Exit fullscreen mode

You get the value sitting in that address (the actual slice)

Why pointers matter
This was the part that did not click for me initially.

Avoiding unnecessary copying
In Go, when you pass data into a function, it gets copied. For small data, this is fine. For larger struct, it can become inefficient.

func process(persons []Person) {
    // this works on a copy
}
Enter fullscreen mode Exit fullscreen mode

If the slice is large, you are copying it every time.

With a pointer:

func process(persons *[]Person) {
    // this works on the same data
}
Enter fullscreen mode Exit fullscreen mode

You are not copying the slice now. You are working with the same data in memory.

A simple efficiency example
Let’s say you have a large struct:

type VeryLargeData struct {
    Values [100000]int
}
Enter fullscreen mode Exit fullscreen mode

Now compare this

func update(data VeryLargeData) {
    data.Values[0] = 100
}
Enter fullscreen mode Exit fullscreen mode

Every time you call this function, Go copies the entire struct.

Now with a pointer:

func update(data *LargeData) {
    data.Values[0] = 100
}
Enter fullscreen mode Exit fullscreen mode

No copying. You are modifying the original data.

This is one of the main reasons pointers matter in Go.

Sharing state
Pointers also make it possible to work on the same data across different parts of your code.

func updateAge(p *Person) {
    p.Age = 30
}
Enter fullscreen mode Exit fullscreen mode

This updates the original struct.

Without a pointer, you would only modify a copy.

Allowing nil values
Pointers can also be nil, which is useful when a value is optional.

var p *Person

if p == nil {
    fmt.Println("No person yet")
}
Enter fullscreen mode Exit fullscreen mode

Understanding * and &
This part looks confusing at first, but it’s simple.

& means “give me the address”

&persons
Enter fullscreen mode Exit fullscreen mode

* means “give me the value at this address.”

*p
Enter fullscreen mode Exit fullscreen mode

One important thing
Slices in Go already behave like references in many cases. So you don’t always need a pointer to a slice.
But you might still use one if you want to:

  • modify the slice itself.
  • make it clear you are sharing data

When to use pointers
A simple way to think about it:

Use pointers when you are dealing with larger data or need to share state.
For smaller values, you really do not need them.

Final mental model
If you remember one thing, let it be this:

Variables store values.
Pointers store where those values live (memory address).

& gets the address.
* gets the value at that address.

Conclusion
Pointers are not hard. They just feel unnecessary until you understand what problems they solve. Once that clicks, they start to make sense.

Top comments (0)