Skip to content

Intermediate · Pointers, interfaces and errors · topic 16 of 46 · 35 min

Pointers without fear

Explanation

A pointer holds the memory address of a value instead of the value itself. &x gives the address of x, and *p reads or changes the value a pointer points to. Go pointers are safe: there is no pointer arithmetic and the garbage collector frees memory for you. Use pointers to let a function change something, to avoid copying big structs, or to show that a value may be absent (nil).

What a pointer is

Every variable lives somewhere in memory, at an address. A pointer is a variable whose value is such an address. The type *int means "pointer to an int". Two operators do all the work: & (address of) and * (the value at).

Think of it like this: Your house is the value; your address on a courier slip is the pointer. Copying the slip is cheap, and anyone with the slip can find the same house. If a painter follows the slip and paints the house blue, everyone who has a copy of that slip will find a blue house.
go
x := 10
p := &x          // p holds the address of x; its type is *int
fmt.Println(*p)  // 10: read through the pointer
*p = 25          // change x through the pointer
fmt.Println(x)   // 25
fmt.Println(p)   // something like 0xc000012345 (an address)
& makes a pointer, * follows it. This is called dereferencing.

Why use pointers?

  1. To let a function change the caller's variable. Go passes arguments as copies, so without a pointer the function only changes its copy.
  2. To avoid copying a large struct every time it is passed or returned.
  3. To share one value between several parts of the program, for example one *Config or one *sql.DB.
  4. To say "no value": a nil pointer can mean "not set", different from a zero value. For example *int can tell "0 runs" from "not played yet".
go
func addBonus(marks *int, bonus int) {
	*marks += bonus
}

func main() {
	m := 80
	addBonus(&m, 5)
	fmt.Println(m) // 85
}
Pass the address with &m; the function changes the value with *marks.

nil pointers

The zero value of a pointer is nil: it points nowhere. Following a nil pointer crashes the program with "invalid memory address or nil pointer dereference". This is the most common Go crash. Check for nil when a pointer may be empty.

go
var p *int
fmt.Println(p == nil) // true
// fmt.Println(*p)    // panic: runtime error: invalid memory address or nil pointer dereference

if p != nil {
	fmt.Println(*p)
}
Guard reads with a nil check when nil is possible.

new, and returning pointers to local variables

go
n := new(int)     // allocates an int set to 0, returns *int
*n = 7

func newCounter() *int {
	c := 0
	return &c        // perfectly safe in Go
}
In C, returning the address of a local variable is a serious bug. In Go the compiler notices (escape analysis) and keeps the value alive on the heap.

In practice you rarely call new. For structs, &Student{Name: "Asha"} is the common way to get a pointer to a new value.

What Go does NOT allow

  • No pointer arithmetic: you cannot write p++ to move to the next address (except with the unsafe package, which normal code never needs).
  • No manual free(): the garbage collector frees memory once no pointer refers to it.
  • No dangling pointers to freed memory in normal Go code.

Pointers and slices, maps and channels

Slices, maps and channels already contain internal pointers to their data, so you almost never need *[]int or *map[string]int. Pass them as they are. (A function that appends to a slice should return the new slice, as you learned in module 3.)

Careful: Common mistake: using pointers everywhere "for speed". Small values (ints, small structs, strings) are cheap to copy, and pointers can make the garbage collector work harder and hide who changes what. Use a pointer when you need one of the four reasons above.
Tip: Method calls handle & and * for you: if s is a Student variable, s.AddGrace(2) works even when AddGrace has a *Student receiver, and p.Name works when p is a *Student.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Pointers without fear · 1/7

Pointers

A pointer holds the address of a value, not the value itself.

Prefer a video by a teacher? Find free videos on “golang pointers explained for beginners & and *” on YouTube ↗

In real life: Optional fields in a cricket score API

A cricket scoring API sends a player's strike rate. Before the player faces a ball, the strike rate does not exist yet, which is different from a strike rate of 0. Using *float64 lets the code store nil for "not batted yet". When this struct is turned into JSON, nil becomes null, so the app can show "-" instead of a misleading 0.0.

go
package main

import "fmt"

type Batter struct {
	Name       string
	StrikeRate *float64 // nil = has not batted yet
}

func show(b Batter) {
	if b.StrikeRate == nil {
		fmt.Println(b.Name, "SR: -")
		return
	}
	fmt.Printf("%s SR: %.1f\n", b.Name, *b.StrikeRate)
}

func main() {
	sr := 142.5
	show(Batter{Name: "Rohit", StrikeRate: &sr})
	show(Batter{Name: "Jadeja"})
}
Prints Rohit SR: 142.5, then Jadeja SR: -

Practice (0/4 done)

Task 1 · Question

What does this print? a := 5 p := &a *p = *p * 2 fmt.Println(a)

Task 2 · Question

What does this print? func reset(n *int) { n = nil } ... x := 3 reset(&x) fmt.Println(x)

Task 3 · Question

Which line crashes? 1: var s *Student 2: fmt.Println(s == nil) 3: fmt.Println(s.Name)

Task 4 · Practice

Write func swap(a, b *int) that swaps two integers in the caller. Then write func applyDiscount(price *int, percent int) that lowers the price in place. Call both from main and print the results.

Quick quiz

Question 1

1.What is the type of &x when x is a float64?

Question 2

2.What is the zero value of a pointer?

Question 3

3.Can you do p++ on a pointer in normal Go code?

Question 4

4.Is it safe to return &local from a function?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is the type of &x when x is a float64?

Answer: *float64

& on a float64 gives a *float64.

What is the zero value of a pointer?

Answer: nil

An unset pointer is nil.

Can you do p++ on a pointer in normal Go code?

Answer: No, Go has no pointer arithmetic

Pointer arithmetic is not allowed (outside the unsafe package).

Is it safe to return &local from a function?

Answer: Yes, the compiler moves it to the heap if needed

Escape analysis keeps the value alive as long as it is referenced.

How does Go provide polymorphism without inheritance?Related

Answer: Interfaces satisfied implicitly

Any type with the right methods can be used through an interface.

From Go language interview questions

For a struct pointer s, what is s->name the same as?Related

Answer: (*s).name

The arrow operator dereferences the pointer and then picks the member.

From Pointers, the stack and the heap

What does slices.Index(s, x) return when x is not in s?Related

Answer: -1

Like many Go search functions, it returns -1 for "not found".

From Slices in depth: length, capacity and sharing

How should a pointer be printed with printf?Related

Answer: %p with a (void *) cast

%p expects a void pointer.

From Addresses and pointers

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Pointers, interfaces and errors” before its exam: Interfaces: behaviour, not family trees, any, type assertions and type switches, Errors as values: wrap, Is and As, panic and recover: only for the truly unexpected.
  2. Module 4 exam: Pointers, interfaces and errors · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Packages, testing and generics. 46 topics left in the course before the final exam.

← Previous: Structs and methods