In Go when I have an array, or slice, of structs, I prefer to always make that an array of pointers to the structs. You might want to do this so you’re not copying the entire struct every time you append to the slice. Using pointers is nanoseconds faster, but the real reason is developer ergonomics! It’s cumbersome to make changes to an array of structs.

For example if you have the following struct.

type Object struct {
	Value int
}

func (o *Object) Double() {
	o.Value *= 2
}

func (o *Object) String() string {
	return fmt.Sprintf("{%d}", o.Value)
}

If you want to change the value of the contents of the array, you would instinctively write something like this.

package main

import (
	"fmt"
)

func main() {
	array := []Object{
		Object{
			Value: 0,
		},
		Object{
			Value: 1,
		},
		Object{
			Value: 2,
		},
	}

	fmt.Println(array)

	for _, item := range array {
		item.Double()
	}

	fmt.Println(array)
}

It won’t work. When you iterate over array, item is a copy of the struct that’s in the array. Any changes we make to it are scoped to our for loop.

If you want to make changes to the array, you need to assign the updated struct to its index in the array.

for i := 0; i < len(array); i++ {
	item := array[i]
	item.Double()
	array[i] = item
}

fmt.Println(array)

If you use pointers in your arrays, you don’t have this problem. The item variable is a pointer, and you can manipulate the struct directly.

pointerArray := []*Object{
	&Object{
		Value: 0,
	},
	&Object{
		Value: 1,
	},
	&Object{
		Value: 2,
	},
}

fmt.Println(pointerArray)

for _, item := range pointerArray {
	item.Double()
}

fmt.Println(pointerArray)

Here’s a Go Playground to play around with it.