Pointers

A pointer holds the address of a value: & takes the address, * follows it, and Go deliberately has no pointer arithmetic.

Syntaxvar p *int = &x // p holds the address of x; *p reads or writes x

A pointer is the address of a value, not the value itself. If a value is a house, a pointer is the address written on an envelope. Two parts of a program can hold the same address, so both see the same house.

You take an address with &. If x is an int, then &x has the type *int, read as "pointer to int". You go back the other way with *. The expression *p is the value stored at the address that p holds, and *p = 7 changes the original x.

Why they exist

Go passes arguments by copying them. A function that takes a Counter receives its own copy, so it cannot change the caller's. Give it a *Counter and it can. Pointers also save copying a large value on every call.

What Go leaves out

There is no pointer arithmetic. You cannot add 1 to a pointer to step to the next value. A pointer can only be taken, followed, compared with ==, and compared against nil. nil is the zero value of every pointer type: the value a pointer has before you give it an address.

Returning the address of a local variable is safe here. The compiler sees that the value outlives the function and places it where it survives. It is reclaimed later by the garbage collector, the part of the Go runtime that frees memory nothing points at any more.

What trips people up

Field access follows the pointer for you. p.Balance is shorthand for the longhand (*p).Balance; both are legal and mean the same thing. Following a nil pointer stops the program.

Example

Example · go
package main

import "fmt"

// Counter holds a single number.
type Counter struct {
	n int
}

// bump takes an address, so it changes the caller's Counter.
func bump(c *Counter) {
	c.n++ // shorthand for (*c).n++
}

func main() {
	c := Counter{n: 0}
	bump(&c) // &c is the address of c
	bump(&c)
	fmt.Println(c.n)

	x := 10
	p := &x // p is of type *int
	*p = 25 // write through the address
	fmt.Println(x)
	fmt.Println(p == &x) // two addresses may be compared
}

// Output:
// 2
// 25
// true

When to use it

  • Let a function fill in defaults on a Config the caller owns.
  • Pass a large struct into a function without copying it on every call.
  • Use a nil *Options to mean "the caller supplied nothing".
  • Edit structs stored in a slice in place with &items[i].

More examples

nil is the zero value of a pointer

A pointer parameter can carry one extra piece of information that a plain value cannot: absence. Reach for this when a function has an optional settings argument and 0 or the empty string would be a real value rather than a missing one. The guard must come before any field access, because reading c.Port on a nil pointer stops the program.

Example · go
package main

import "fmt"

type Config struct {
	Port int
}

// port reads through a pointer and treats nil as "nothing supplied".
func port(c *Config) int {
	if c == nil {
		return 8080
	}
	return c.Port
}

func main() {
	var missing *Config // declared but never given an address, so nil
	fmt.Println(port(missing))
	fmt.Println(port(&Config{Port: 9000}))
}

// Output:
// 8080
// 9000

Pointing at an element inside a slice

This is the standard way to edit structs that live inside a slice. Writing for _, it := range items and then it.Count *= 10 edits a copy and leaves the slice untouched, which is one of the most common early Go bugs. Taking &items[i] gives you the element itself.

Example · go
package main

import "fmt"

type Item struct {
	Name  string
	Count int
}

func main() {
	items := []Item{{Name: "nail", Count: 1}, {Name: "screw", Count: 2}}

	// items[i] has its own address, so &items[i] points at the real element.
	for i := range items {
		p := &items[i]
		p.Count *= 10
	}
	fmt.Println(items)

	// Assigning an element copies it. Changing the copy changes nothing else.
	first := items[0]
	first.Count = 0
	fmt.Println(items[0].Count)
}

// Output:
// [{nail 10} {screw 20}]
// 10

Discussion

  • Be the first to comment on this lesson.