Pointers
Pointers in Go — the `&` and `*` operators, value vs reference semantics, the nil pointer, and when a pointer is actually needed.
4 questions
JuniorTheoryVery commonDoes Go pass function arguments by value or by reference?
Does Go pass function arguments by value or by reference?
Go always passes arguments by value — the function receives a copy, so writing to a parameter never touches the caller's variable. To mutate the caller's variable, pass a pointer *T and write through it. A slice, map, or channel is also copied, but the copied header still shares the same backing array or table, so element edits are visible to the caller.
Common mistakes
- ✗Calling slices and maps "pass by reference" instead of copied headers over shared backing
- ✗Expecting a value parameter's field write to reach the caller
- ✗Forgetting that mutation needs a pointer
*Tpassed explicitly
Follow-up questions
- →Why does appending to a slice inside a function sometimes not show up outside?
- →How does a pointer receiver method differ from a value receiver here?
JuniorTheoryCommonWhat do the & and * operators do with pointers in Go?
What do the & and * operators do with pointers in Go?
&x takes the address of x and yields a pointer of type *T. *p dereferences a pointer to read or write the value it points at. A pointer's zero value is nil, meaning it points at nothing yet. Go has no pointer arithmetic — you cannot add to or subtract from a pointer the way C allows.
Common mistakes
- ✗Swapping the operators — thinking
&dereferences and*takes an address - ✗Expecting C-style pointer arithmetic to work on a Go pointer
- ✗Forgetting that a pointer's zero value is
nil, not address zero
Follow-up questions
- →What is the difference between
*Tin a type and*pin an expression? - →Why does Go deliberately leave out pointer arithmetic?
JuniorTheoryCommonWhat happens when you dereference a nil pointer in Go?
What happens when you dereference a nil pointer in Go?
Dereferencing a nil pointer triggers a runtime panic: invalid memory address or nil pointer dereference. nil is the zero value of any pointer, so an unassigned pointer holds nil until you point it somewhere. Guard with if p != nil before reading *p, or recover the panic at a boundary. There is no silent null read like C might give.
Common mistakes
- ✗Expecting a silent zero-value read instead of a panic
- ✗Thinking the nil dereference is caught at compile time
- ✗Skipping the
if p != nilguard before reading*p
Follow-up questions
- →How can
recoverturn a nil-pointer panic into a handled error? - →Why can a nil pointer of a concrete type sit inside a non-nil interface?
MiddleTheoryCommonWhen should a function take a pointer rather than a value in Go?
When should a function take a pointer rather than a value in Go?
Take a *T when the function must mutate the caller's value, or to avoid copying a large struct on every call. Prefer a value for small immutable data — it keeps the API copy-safe, so the callee cannot alter the caller's state. Passing a pointer also lets the callee observe nil to mean "absent", which a value parameter can never express.
Common mistakes
- ✗Defaulting to pointers everywhere and losing the copy-safety of value parameters
- ✗Believing a value parameter can mutate the caller without a pointer
- ✗Overlooking that only a pointer parameter can carry
nilfor "absent"
Follow-up questions
- →How does this guidance map onto choosing a value vs pointer method receiver?
- →At what struct size does copy cost actually start to matter in practice?