Learn Go
beginner1 min read

Pointers

A pointer holds the memory address of a value. Go pointers give you control over sharing and mutation without the complexity of manual memory management — the garbage collector handles deallocation automatically.

The & and * Operators

package main
 
import "fmt"
 
func main() {
    x := 42
    p := &x // p is *int
 
    fmt.Println(x)  // 42
    fmt.Println(p)  // 0xc0000b4008 (some memory address)
    fmt.Println(*p) // 42
 
    *p = 100 // modify x through p
    fmt.Println(x) // 100
}

Why Pointers Matter

Functions receive arguments by value — a copy is made. Use a pointer to let a function modify the caller's variable:

package main
 
import "fmt"
 
func doubleValue(n int) {
    n *= 2 // modifies the copy only
}
 
func doublePointer(n *int) {
    *n *= 2 // modifies the original
}
 
func main() {
    x := 5
    doubleValue(x)
    fmt.Println(x) // 5 — unchanged
 
    doublePointer(&x)
    fmt.Println(x) // 10 — modified
}

Pointer Receivers

Pointer receivers allow methods to modify their receiver and are more efficient for large structs:

package main
 
import "fmt"
 
type Stack struct {
    items []int
}
 
func (s *Stack) Push(v int) {
    s.items = append(s.items, v)
}
 
func (s *Stack) Pop() (int, bool) {
    if len(s.items) == 0 {
        return 0, false
    }
    last := len(s.items) - 1
    v := s.items[last]
    s.items = s.items[:last]
    return v, true
}
 
func (s Stack) Len() int {
    return len(s.items)
}
 
func main() {
    var st Stack
    st.Push(1)
    st.Push(2)
    st.Push(3)
 
    fmt.Println(st.Len()) // 3
 
    if v, ok := st.Pop(); ok {
        fmt.Println("popped:", v) // popped: 3
    }
    fmt.Println(st.Len()) // 2
}

Idiomatic Go: Go automatically takes the address when you call a pointer-method on an addressable value. st.Push(1) and (&st).Push(1) are equivalent.

The new Function

new(T) allocates a zeroed T and returns a *T. It is less common than composite literals with &:

package main
 
import "fmt"
 
type Config struct {
    Debug   bool
    Workers int
}
 
func main() {
    c1 := new(Config)        // &Config{false, 0}
    c2 := &Config{Debug: true, Workers: 4} // idiomatic
 
    fmt.Println(*c1) // {false 0}
    fmt.Println(*c2) // {true 4}
}

Nil Pointers

A pointer's zero value is nil. Dereferencing nil causes a panic — always check before use:

package main
 
import "fmt"
 
type Node struct {
    Value int
    Next  *Node
}
 
func printList(n *Node) {
    for n != nil {
        fmt.Print(n.Value, " ")
        n = n.Next
    }
    fmt.Println()
}
 
func main() {
    head := &Node{Value: 1, Next: &Node{Value: 2, Next: &Node{Value: 3}}}
    printList(head) // 1 2 3
}

Key Takeaways