A gentle introduction to Pointers

 

When you assign a value to a variable in Go, the value is stored at a particular address in your computer's memory.

You can use the reference operator & to get this address, like so:

package main

import "fmt"

func main() {
    var answer int = 42

    fmt.Println(&answer) // Prints 0xc00001c030 or similar
}

When I run this code on my machine, it prints out 0xc00001c030, which is the memory address of the answer variable (expressed as a hexadecimal number). If you try running this code on your own computer, or on the Go playground, you'll probably get a different value.

What is a pointer?

When you use the & operator on a variable it actually returns a pointer.

In simple terms, a pointer is a variable that holds the memory address of another variable. You can think of them as "pointing to" a specific spot in memory.

Like the variables that they point to, pointers in Go also have types. A pointer with the type *int can only hold the memory address of an int variable. And a pointer with the type *string can only hold the memory address of a string variable. And so on.

That might sound a bit confusing, so to illustrate let's rewrite the example above so that we assign &answer to a variable instead of printing it directly. I'll use longhand variable declarations here (without type inference) to help make it explicit.

package main

import "fmt"

func main() {
    // Declare an answer variable with the type int and assign the value 42.
    var answer int = 42

    // Declare an answerPtr variable with the type *int. Use the & operator to
    // get a pointer to the answer variable and assign it as the value.
    var answerPtr *int = &answer

    fmt.Println(answerPtr) // Prints 0xc00001c030 or similar
}

So, in this example, the answerPtr variable is a pointer with the type *int, which contains the memory address of the answer variable.

We can re-write this to use the more common := shorthand operator for the variable declarations like so:

package main

import "fmt"

func main() {
    answer := 42

    answerPtr := &answer

    fmt.Println(answerPtr)
}

Dereferencing a pointer

You can use the dereference operator * to read or set the underlying value that a pointer points to. This is often known as indirection.

Here's an example of using the dereference operator to read an underlying value:

package main

import "fmt"

func main() {
    answer := 42
    answerPtr := &answer

    fmt.Println(answerPtr)  // Prints 0xc00001c030 or similar
    fmt.Println(*answerPtr) // Prints 42
}

Remember, the answerPtr variable in the example above points to the answer variable. By using the dereference operator *answerPtr we can 'read through' to get the value of the answer variable and print it out.

And we can also use the dereference operator to 'write through' and assign a new underlying value. Like so:

package main

import "fmt"

func main() {
    answer := 42
    answerPtr := &answer

    *answerPtr = 99     // Use the dereference operator to assign a new value
    fmt.Println(answer) // Prints 99
}
If you enjoyed this post...

You might like to check out my other Go tutorials on this site, or if you're after something more structured, my books Let's Go and Let's Go Further cover how to build complete, production-ready web apps and APIS with Go.

Not sure how to structure your Go web application?
My new book guides you through the start-to-finish build of a real world web application in Go — covering topics like how to structure your code, manage dependencies, create dynamic database-driven pages, and how to authenticate and authorize users securely.