When to use pointers
Fast recall
Quick revision
Core answer
nil has a deliberate meaning such as 'not supplied'. Value parameters and receivers work on a copy and are ideal for small immutable-style data. Pointer forms share one value and support mutation, but they introduce nil handling and make ownership less local.Give a selection rule for pointers, show a practical example, and explain when a value is clearer.
Saying “it depends” without naming the condition that changes the choice.
State the default choice and the specific reason to move away from it.
Interview-ready guide
Interview answer
Use a pointer when mutation must be visible to the caller, when copying a large struct would be wasteful, or when
nilhas a deliberate meaning such as 'not supplied'. Choose it because the behaviour is part of the requirement, not simply because the syntax is available.For example: the function receives the address of
count, dereferences it, and changes the original value. Passingcountwithout&would give the function a copy instead. This small case shows the reason for the choice directly.Value parameters and receivers work on a copy and are ideal for small immutable-style data. Pointer forms share one value and support mutation, but they introduce nil handling and make ownership less local. The two choices solve related problems but communicate different guarantees.
Use a pointer when mutation must be visible to the caller, when copying a large struct would be wasteful, or when
nilhas a deliberate meaning such as 'not supplied'. Otherwise, use a value when it keeps the code simpler.
When to use pointers
Direct Answer
Use a pointer when mutation must be visible to the caller, when copying a large struct would be wasteful, or when nil has a deliberate meaning such as 'not supplied'. Value parameters and receivers work on a copy and are ideal for small immutable-style data. Pointer forms share one value and support mutation, but they introduce nil handling and make ownership less local.
Deep Dive & Architecture
For a nil-pointer panic, start at the dereference in the stack trace and move backward to the assignment or function return that produced nil.
Check constructor results, map lookups that return pointer values, optional request fields, interface values that may hold typed nil pointers, and concurrent mutation.
Use a pointer when mutation must be visible to the caller, when copying a large struct would be wasteful, or when nil has a deliberate meaning such as 'not supplied'. Value parameters and receivers work on a copy and are ideal for small immutable-style data. Pointer forms share one value and support mutation, but they introduce nil handling and make ownership less local.
The common mistake is dereferencing a nil pointer or using a pointer when ordinary value semantics would be simpler. A nil pointer has no value behind it, so *p or p.Field can panic. Check optional pointers at boundaries, return useful errors with nil results, use value semantics by default for small data, and choose one receiver style consistently for a type.
Code Example
func increment(n *int) {
*n++
}
count := 2
increment(&count)
fmt.Println(count) // 3
Common Mistakes & Pitfalls
Saying “it depends” without naming the condition that changes the choice.
Check your understanding
Answer these without looking back at the article.
- What are pointers in Go, and how do they work?
- How do pointers compare with a value in Go?
- What common mistakes should you avoid with pointers in Go?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.