This guide explains what golang ampersand (&) does, how it pairs with * for pointers, and how that shows up in real code (functions, structs, and a few sharp edges). For a longer tutorial on pointers, see Go pointers tutorial.
Go 1.24 on Linux.
Quick answer: what does & mean in Go?
The unary & operator takes the address of its operand. If x is a variable of type T, then &x has type *T (read “pointer to T”) and points at the storage for x. The unary * in an expression dereferences a pointer: *p is the value p points to. In a type, *T means “pointer to T”—the same character with a different role.
Ampersand and pointers in Go
What is a pointer?
A pointer value holds the address of another value. The zero value of a pointer is nil, meaning it does not point at any valid memory.
What does &variable return?
&variable evaluates to a pointer to that variable. The variable must be addressable (see the address operators in the Go spec).
Pointer types such as *int, *string, *User
Writing *int in a declaration introduces a variable that holds a pointer to an int, not an int value itself:
package main
import "fmt"
func main() {
n := 2022
var p *int
p = &n
fmt.Println("n:", n, "via pointer:", *p)
*p = 19
fmt.Println("n after assign through p:", n)
}You should see n: 2022 and via pointer: 2022, then n after assign through p: 19.
& and * together
&x— address ofx.*T— type “pointer toT”.*p— value at the address stored inp(dereference).
Go does not support C-style pointer arithmetic: you cannot add integers to a pointer to walk memory.
Ampersand in function arguments
Passing &count lets a function change the caller’s variable through a *int parameter:
package main
import "fmt"
func main() {
x, y := 5, 6
fmt.Println("before", x, y)
swapValues(x, y)
fmt.Println("after swapValues", x, y)
swapPointers(&x, &y)
fmt.Println("after swapPointers", x, y)
}
func swapValues(a, b int) {
a, b = b, a
}
func swapPointers(a, b *int) {
*a, *b = *b, *a
}You should see after swapValues still 5 6 (arguments are copied), then after swapPointers as 6 5 because the function mutates the original x and y through their addresses.
Pointer parameters are common for large structs (avoid copying), for optional results (error patterns), and whenever the callee must modify caller state.
Ampersand with structs
Taking the address of a struct literal is idiomatic when you want a *User without a separate var:
package main
import "fmt"
type User struct {
Name string
}
func main() {
u := &User{Name: "Deepak"}
fmt.Println(u.Name)
}You should see Deepak. Field access u.Name works on pointers to structs (Go inserts an implicit dereference); (*u).Name is equivalent but rarely needed.
Slices, maps, channels, and pointers
Slices, maps, and channels are already small reference-like headers: copying the header copies a descriptor, not the whole backing array or map. You usually do not pass &slice into APIs unless you intentionally want a pointer to the header (for example to resize a slice in the caller, or for json.Unmarshal into a pointer). Prefer plain []T, map[K]V, or chan T parameters unless a library or design doc asks for a pointer.
Address vs value
| You write | Meaning |
|---|---|
x |
the value in variable x |
&x |
address of x |
p := &x |
p is a pointer to x |
*p |
value stored where p points |
*p = v |
store v through p into the pointed-to variable |
Mistakes to avoid
- Taking
&of something not addressable, for example a constant literal:
package main
func main() {
_ = &42
}Building that reports an error like cannot take address of 42 (untyped int constant).
- Dereferencing a
nilpointer at runtime (var p *int; fmt.Println(*p)panics). Always ensure a pointer is non-nil before*p, or guard with checks. - Confusing
*int(a type) with*p(dereference of variablep). Context tells you which meaning applies. - Defaulting to pointers for tiny values (
int,bool) when a plain value is simpler and clearer.
Go & and * cheat sheet
| Expression | Meaning |
|---|---|
x |
value stored in variable x |
&x |
address of x |
p := &x |
p holds a pointer to x |
*int |
type: pointer to int |
*p |
value at address in p |
*p = 20 |
write through the pointer |
&User{...} |
pointer to a new struct value |
References
- The Go Programming Language Specification — Address operators
- The Go Programming Language Specification — Pointer types
- Go pointers tutorial (deeper walkthrough on this site)
Summary
In Go, & takes the address of an addressable variable and yields a pointer. * in a type means pointer-to-that-type; *p in code reads or writes through the pointer. Together they let functions mutate caller state, build struct pointers with &T{...}, and share large values efficiently. Go omits pointer arithmetic; watch for non-addressable operands, nil dereferences, and unnecessary pointers to slice/map/channel headers when a value argument is enough.

