Pointers Explained

Overview

Pointers hold memory addresses. In Go, they’re simple and safe—no pointer arithmetic allowed.

Pointer Basics

x := 42
p := &x      // p is *int, holds address of x
fmt.Println(*p)  // 42 (dereference)
*p = 100     // Modify x through pointer
fmt.Println(x)   // 100

Declaration

var p *int       // nil pointer
p = &x           // Point to x

// Classic new (zero value)
p := new(int)    // *int to 0

// Expression-based new (Go 1.26+)
p := new(42)     // *int to 42
Stack/heap view (simplified)

x: 42          p: 0xABC
 |               |
 +------ addr ---+
         |
       [42]

Why Use Pointers

1. Modify Variables in Functions

func increment(n *int) {
    *n++
}

x := 1
increment(&x)
fmt.Println(x)  // 2

2. Avoid Copying Large Structs

type BigStruct struct {
    Data [1000]int
}

// Bad: copies entire struct
func process(b BigStruct) { }

// Good: passes pointer (8 bytes)
func process(b *BigStruct) { }

3. Share Data

type Config struct {
    Debug bool
}

func loadConfig() *Config {
    return &Config{Debug: true}
}

cfg := loadConfig()  // All code shares same Config

nil Pointers

var p *int  // nil

if p != nil {
    fmt.Println(*p)  // Safe
}

// Dereferencing nil panics!
// *p = 1  // panic: runtime error

Safe pattern for optional fields in APIs:

type UpdateUserRequest struct {
    Name  *string `json:"name,omitempty"`
    Email *string `json:"email,omitempty"`
}

Pointer to Pointer

x := 42
p := &x
pp := &p  // **int

**pp = 100
fmt.Println(x)  // 100

Pointers and Slices/Maps

Slices and maps are already reference types:

func modify(s []int) {
    s[0] = 100  // Modifies original
}

nums := []int{1, 2, 3}
modify(nums)
fmt.Println(nums[0])  // 100

// But reassigning needs pointer
func replace(s *[]int) {
    *s = []int{4, 5, 6}
}

Common Patterns

Return Pointer for “No Result”

func find(id int) *User {
    if found {
        return &user
    }
    return nil  // Not found
}

if user := find(1); user != nil {
    // Use user
}

Constructor Pattern

func NewUser(name string) *User {
    return &User{
        Name:      name,
        CreatedAt: time.Now(),
    }
}

Summary

Operation Syntax
Get address &x
Dereference *p
Allocate new(T)
Check nil p != nil