Pointers & the Address-of Operator
Pointers hold the address of a value, enabling mutation across function boundaries and avoiding large copies.
Search across all documentation pages
Pointers hold the address of a value, enabling mutation across function boundaries and avoiding large copies.
Go has pointers but no pointer arithmetic, keeping memory safety straightforward.
&x takes the address of x; *p dereferences pointer p.
Function arguments are passed by value; share mutation by passing pointers.
Method receivers can be values or pointers; pointer receivers are required when methods modify state.
Quick-reference recipe card - copy-paste ready.
package main
import "fmt"
func increment(n *int) {
*n++
}
func main() {
count := 10
increment(&count)
fmt.Println(count)
}When to reach for this:
*T.package main
import "fmt"
type Config struct {
Host string
Port int
}
func (c *Config) SetPort(p int) {
c.Port = p
}
func clone(cfg Config) Config {
return cfg // struct copied by value
}
func main() {
cfg := Config{Host: "localhost", Port: 8080}
cfg.SetPort(9090)
copy := clone(cfg)
copy.Port = 1
fmt.Println(cfg.Port, copy.Port)
var p *int
if p != nil {
fmt.Println(*p)
} else {
fmt.Println("p is nil")
}
}What this demonstrates:
SetPort mutates cfg in place.copy.Port does not affect cfg.& is not needed when calling pointer-receiver methods on addressable values.&.new(T) allocates zeroed T and returns *T; idiomatic code often uses &T{}.nil pointers compare equal to each other and to untyped nil in typed pointer context.nil interface.| Aspect | By value T | By pointer *T |
|---|---|---|
| Copy cost | Whole struct | Pointer word |
| Mutation in callee | No (unless return) | Yes via dereference |
nil possible | No for value | Yes |
| JSON omitempty | N/A | Omits nil pointers |
| Use pointer receiver when | Use value receiver when |
|---|---|
| Method mutates receiver | Small immutable type |
| Struct is large | Consistency with existing API |
| Must share mutex state | Zero-value methods must work (time.Time) |
// Optional field
type User struct {
Name string
Nick *string // nil means unset
}
// Do not pass pointer to loop variable without copying
for i := 0; i < 3; i++ {
n := i
go func() { fmt.Println(&n) }()
}*p when p == nil panics. Fix: check p != nil or guarantee construction.return (*T)(nil), err still makes interface non-nil. Fix: return nil interface or concrete value type.item := item inside loop.| Alternative | Use When | Don't Use When |
|---|---|---|
| Return new value | Immutable transforms | Large structs or many mutations |
Pointer *T | Mutation, optional fields | Tiny ints/bools (use value) |
| Interface | Hide concrete type | Need nil vs value distinction clearly |
sync/atomic | Concurrent counters | General struct fields |
No - you cannot increment pointers like in C.
Use slices for buffer traversal.
Go automatically takes address for pointer receivers when value is addressable: cfg.SetPort(1) calls (&cfg).SetPort(1).
No - &m[key] is invalid because map storage may move.
Copy to a local variable and point at that if needed temporarily.
new(T) returns *T to a zero-initialized T.
make is only for slices, maps, and channels.
Pointer values compare with == and != if they point to the same variable or both nil.
They do not compare slice contents.
Taking &local may escape local to the heap if the pointer outlives the function.
The compiler decides; you cannot force stack allocation reliably.
*string with omitempty omits unset fields in JSON.
Use pointers for optional API fields; values for required ones.
Pointer to pointer - rare in application code.
Appears in C interop or some generic APIs.
Yes - escape analysis moves locals to the heap when their address escapes.
This is safe in Go unlike stack-returning in C.
Set to nil or replace with &T{} for fresh zero value.
Choose based on whether absent vs empty matters to callers.
Mutex must not be copied after first use.
Pointer receivers on types embedding Mutex prevent accidental copy by value methods.
Low-level escape hatch for interop and advanced optimizations.
Avoid in application code unless you truly need unsafe package rules.
newStack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 18, 2026