& 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 amap[string]*Tfor a missing key, which returnsnil- ignoring an error:
f, _ := os.Open(path)leavesfnil 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 (*intdistinguishes "absent" from0)
Do not use a pointer when:
- the value is small and read-only (
time.Time, aPoint) - the type is already a reference-like value: slices, maps, channels, functions and interfaces rarely need
*in front of them. A*[]intor*map[string]intis 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 == nilwherever nil is possible. - Pointer to a range value.
&vpoints at a copy; use&s[i]. - Pointers to slices and maps. Rarely needed. Return the new slice instead.
- Holding
&s[i]across anappend. Ifappendreallocates, 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.