Small Lesson About Pointers & Slices in Go
Tags: go
Published: 2019-02-02 02:05:45

---
title: Small Lesson About Pointers & Slices in Go
published: true
description: Pointers and slices in Go can be deceiving if you're not paying attention
tags: go
---

Today I learned a little bit more about how pointers work in Go.

We had this object that we want other functions to directly modify. So we pass a pointer to this object to various functions and then in our tests we check to see that the original object has been modified correctly. We also have these helper functions that are supposed to return a pointer to an object buried inside of the `struct`.

Here's an example of our object:

```golang
type obj struct {
	num   int
	str   string
	slice []int
}
```

And then we had a helper function that is supposed to return a reference to an **element inside of the `slice`**.

Working with our example object, I had a helper function that looked like this:

```golang
func FindNumber(object *obj, findInt int) *int {
	for _, singleInt := range object.slice {
		if singleInt == findInt {
			return &singleInt
		}
	}
	return nil
}
```

It's supposed to go into a given object and return a pointer to the matching element inside the `obj.slice`  parameter. If we put this together right now we get this program:

```golang
package main

import "fmt"

type obj struct {
	num   int
	str   string
	slice []int
}

func main() {
	myObject := obj{num: 1, str: "hello", slice: []int{1, 2, 3}}

	pointerToNumber := FindNumber(&myObject, 2)

	*pointerToNumber = 5 // change the value from 2 to 5
	fmt.Printf("%p - %v - from FindObject()\n", pointerToNumber, *pointerToNumber)
	fmt.Printf("%p - %v - Pointer address I actually wanted\n", &myObject.slice[1], myObject.slice[1])
}

func FindNumber(object *obj, findInt int) *int {
	for _, singleInt := range object.slice {
		if singleInt == findInt {
			return &singleInt
		}
	}
	return nil
}
```

And when we run it we get this output:

```
0xc00001a0b8 - 5 - from FindObject()
0xc0000182e8 - 2 - Pointer address I actually wanted
```

The value didn't change to 5 in my original object and we can see the pointer address doesn't match. Any changes I make to the value of this pointer won't be reflected in the `myObject` variable.

Let's think about why this is happening, going line by line.

First we make our object:

```golang
myObject := obj{num: 1, str: "hello", slice: []int{1, 2, 3}}
```

and we pass a pointer of our object to `FindNumber()`, telling it to return a pointer to the element within the slice that equals `2`:

```golang
pointerToNumber := FindNumber(&myObject, 2)
```

Inside `FindNumber()` we we get the slice from the pointer and iterate over it:

```golang
for _, singleInt := range object.slice {
	...
}
```

Let's stop and think about this `for` loop. We have a pointer to `object` and we're using that pointer to access the `slice` parameter. Let's keep in mind that a slice is just a **pointer** to an array and not a pointer to each **element** contained in the array.

So when `range` gets an element from whatever slice it's been passed, it **passes the value** of that element to our `singleInt` variable. So `singleInt` is not a pointer to the element, it's just a copy of the original element! If I want to return a pointer to the actual element in the slice I'll have to use the index returned from range and return the pointer from the original slice:

```golang
func FindNumber(object *obj, findInt int) *int {
	for index, singleInt := range object.slice {
		if singleInt == findInt {
			return &object.slice[index]
		}
	}
	return nil
}
```

Now my output is this:

```
0xc0000182e8 - 5 - from FindObject()
0xc0000182e8 - 5 - Pointer address I actually wanted
```

The addresses match and the value changed!

There's also another way to solve this problem that shows some of the dangers with pass-by-value in Go.

If we change `FindNumber()` to not accept a pointer:

```golang
func FindNumber(object obj, findInt int) *int {
	fmt.Printf("%p - address of object in FindNumber\n", &object)
	...
}
```

And not pass in a pointer to our object:

```golang
fmt.Printf("%p - address of myObject\n", &myObject)
pointerToNumber := FindNumber(myObject, 2)
```

and run our code now:

```
0xc000076180 - address of myObject
0xc0000761b0 - address of object in FindNumber
0xc000090028 - 5 - from FindObject()
0xc000090028 - 5 - Pointer address I actually wanted
```

We see that `FindNumber()` got a copy of the original object but the pointer it returned still allowed us to change the value in the original object. So when we pass-by-value any struct, the slices inside the struct point to the same array. And this makes sense because a pointer is just a memory address and copying an address doesn't change the location of the address. If a pointer says `go to 0xc000076180`, when I copy that, my new pointer will also say `go to 0xc000076180`.

If you want to avoid accidentally modifying the original slice then you should create a `copy()` of the slice.

Hope this taught you a little more about slices and pointers in Go.