Difference between using Receiver as copy and pointer (Golang)

This post is to elucidate the fact that, using the receiver in the Golang methods as copy or as pointers can have a huge impact. Let’s get started.

First, let's create books.go file with the following content:

package main

import "fmt"

type books struct {
listing [10000000]string
}

func (b books) printAddr() {
fmt.Printf("%p\n", &b)
}

And, here is the main.go file:

package main

func main() {
var b books

for i := 0; i < 10; i++ {
b.printAddr()
}
}

As you can see from the above snippets, in books.go file we are creating a struct named books with field listing that is a huge array of string. And we create a method printAddr that simply prints the address of the books type which here is passed as a receiver to the method. And in the main.go file we create a variable b which is of type books and we use for loop which iterates 10 times and calls printAddr() method each time.

Now in order to measure the performance, in UNIX based system I run the below command to execute the program:

time go run .

Here are the metrics I received

0xc00a080000
0xc013918000
0xc01d1b0000
0xc013918000
0xc00a080000
0xc01d1b0000
0xc013918000
0xc00a080000
0xc01d1b0000
0xc013918000

real 0m0.494s
user 0m1.170s
sys 0m0.100s

As you can see, the program prints the hex value of the memory address ten times. But our interest lies below that print statement. As you can see if we take real as input it took approximately half a second to run. Now let's make a small adjustment to the books.go file like shown below:

package main

import "fmt"

type books struct {
listing [10000000]string
}

func (b *books) printAddr() {
fmt.Printf("%p\n", &b)
}

The above program is similar to the previous one except for the highlighted change. As you can see, now we are passing a pointer to the receiver. Let’s run the program again and let's see the results. Below is the result I received:

0xc00000e010
0xc009a10008
0xc009a10010
0xc009a10018
0xc009a10020
0xc009a10028
0xc009a10030
0xc009a10038
0xc009a10040
0xc009a10048

real 0m0.138s
user 0m0.156s
sys 0m0.059s

Note I printed the pointer receiver’s address, that’s why the addresses is different. If you just print b instead of &b then the memory address will be the same.

As you can see, the program runs much faster even for this simple program. Think about the implications for the program with more complexity?

Yes, it is evident that passing a pointer to a receiver makes the execution faster. But why? Here is why.

In the first scenario, the receiver is passed by value, which means it is copied each time the method is called. Whereas, in the second scenario, also value is copied but there is a difference. It does not copy the value but it copies the memory address, thus making the program much faster as it does not make a copy of a huge array each time and only hex value of the address.

Caveat — Please don't use pointers with slice, map and other composite types as they already are a pointers.

Happy Learning!