Menu

Golang Pointers: & and *, new, nil and When to Use Them

A pointer holds the address of a value. Learn & and *, new, pointers to structs, why you can safely return a pointer to a local variable, when to use pointers, and the nil pointer dereference panic.

This page includes runnable editors - edit, run, and see output instantly.

& and *

A pointer holds the memory address of a value. &x gives you a pointer to x. *p follows the pointer to the value.

The star has two roles. In a type (*int) it means "pointer to int". In an expression (*p) it means "the value p points to". Printing p itself shows an address like 0xc000012345, which differs between runs.

Why pointers exist: sharing instead of copying

Go passes everything by value. A function gets a copy of each argument, so it cannot change the caller's variable. Passing a pointer copies the address instead, and both sides reach the same value.

Go has no references in the C++ sense. "Pass by reference" in Go means passing a pointer by value.

Pointers to structs

Struct pointers are the most common pointers in Go code. Field access dereferences automatically: p.Name means (*p).Name.

Methods follow the same logic: a pointer receiver lets the method change the struct. The methods page covers the value vs pointer receiver choice.

new

new(T) allocates a zeroed T and returns its address. It is the same as declaring a variable and taking its address.

p := new(int) // *int pointing at 0
var x int
q := &x           // same thing, two lines
a := new(Account) // same as &Account{}

In practice &T{...} is more common for structs because it lets you set fields in the same expression. new is handy for pointers to basic types. Do not confuse it with make, which builds slices, maps and channels and returns them ready to use, not as pointers.

Returning a pointer to a local variable is safe

In C, returning the address of a local variable is a bug. In Go it is ordinary code.

The compiler's escape analysis decides whether a value lives on the stack or the heap. You never choose, and you never free memory: the garbage collector reclaims it when nothing points to it. go build -gcflags=-m prints those decisions if you are curious.

nil pointers

The zero value of any pointer type is nil. Dereferencing nil panics.

Output:

1
2
true
recovered: runtime error: invalid memory address or nil pointer dereference

Without the recover, the program would crash with that message and a stack trace starting with panic: runtime error: invalid memory address or nil pointer dereference and [signal SIGSEGV: segmentation violation ...]. The usual sources:

  • a pointer variable or struct field that was never set
  • m[key] on a map[string]*T for a missing key, which returns nil
  • ignoring an error: f, _ := os.Open(path) leaves f nil on failure
  • a method with a pointer receiver called on a nil pointer, which then reads a field

Calling a method on a nil pointer is itself legal. The panic happens only when the method touches the receiver's fields.

When to use a pointer

Use a pointer when:

  • a function or method must change the caller's value
  • the value is a large struct and is passed around often
  • the type must not be copied (it contains a sync.Mutex, or it represents a unique resource like a connection)
  • you need to express "no value" with nil, for example an optional field in a JSON struct (*int distinguishes "absent" from 0)

Do not use a pointer when:

  • the value is small and read-only (time.Time, a Point)
  • the type is already a reference-like value: slices, maps, channels, functions and interfaces rarely need * in front of them. A *[]int or *map[string]int is almost always a mistake

Pointers are not automatically faster. Copying a small struct is cheap, and a pointer can force a heap allocation and add work for the garbage collector.

Pointers and loop variables

Taking the address of a loop variable is safe since Go 1.22, because each iteration has its own variable:

This prints 0 1 2. Before Go 1.22 (which also introduced range 3), the same loop written as for i := 0; i < 3; i++ printed 3 3 3, since all three pointers shared one variable. Note that &v in for _, v := range items points to a copy of the element, not the element in the slice. To get a pointer to the element itself, use &items[i].

Common mistakes

  • Dereferencing without checking. Check if p == nil wherever nil is possible.
  • Pointer to a range value. &v points at a copy; use &s[i].
  • Pointers to slices and maps. Rarely needed. Return the new slice instead.
  • Holding &s[i] across an append. If append reallocates, the pointer still refers to the old array and later writes go nowhere visible.

Frequently Asked Questions

What do & and * mean in Go?

&x takes the address of x and gives a pointer of type *T. *p dereferences the pointer: it reads or writes the value it points to. In a type, *T means "pointer to T".

What is the difference between new and make in Go?

new(T) allocates a zeroed T and returns a *T; it works for any type. make only works for slices, maps and channels, and returns an initialized (not zeroed, not pointer) value of that type: make(map[string]int) is ready to use, while new(map[string]int) is a pointer to a nil map.

Can I return a pointer to a local variable in Go?

Yes. Unlike C, it is safe. The compiler's escape analysis sees that the variable outlives the function and allocates it on the heap. func newInt() *int { x := 5; return &x } is correct Go.

What causes "invalid memory address or nil pointer dereference" in Go?

Reading or writing through a nil pointer: a *T variable that was never assigned, a map lookup of a missing key in a map[K]*V, or a function that returned nil alongside an error you did not check. Check for nil, or initialize the pointer before use.

Does Go have pointer arithmetic?

No. You cannot add to a pointer or index memory through it. The unsafe package allows it for low-level code, with no safety guarantees. Normal Go code uses slices for contiguous memory.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED