# 4. Nil Pointers

Pointers can be *very* dangerous.

If a pointer points to nothing (the zero value of the pointer type is `nil`) then dereferencing it will cause a runtime error (a [panic](https://gobyexample.com/panic)) that crashes the program. Generally speaking, whenever you're dealing with pointers you should check if it's `nil` before trying to dereference it.

### The Danger

Dereferencing a `nil` pointer causes a **panic** (runtime crash):

```go
var p *int           // p is nil (points to nothing)
fmt.Println(*p)      // ❌ PANIC! Can't dereference nil
```

### Always Check for Nil

```go
var p *int

if p != nil {
    fmt.Println(*p)  // ✓ Safe to dereference
} else {
    fmt.Println("Pointer is nil")
}
```

### Real Example

```go
type user struct {
    name string
    age  int
}

func updateAge(u *user, newAge int) {
    if u == nil {
        fmt.Println("Cannot update nil user")
        return
    }
    u.age = newAge  // Safe to use
}

func main() {
    var bob *user  // bob is nil
    updateAge(bob, 30)  // Without check, this would panic
}
```

### Common Pattern

```go
func processUser(u *user) {
    // Check at the start of function
    if u == nil {
        return  // Early return if nil
    }
    
    // Safe to use u now
    fmt.Println(u.name)
    u.age++
}
```

### Why Pointers Can Be Nil

```go
// Declared but not initialized
var p *int  // nil

// Explicitly set to nil
var p *int = nil  // nil

// Function returns nil
func getUser() *user {
    return nil  // Might return nil if user not found
}
```

### Safe vs Unsafe

**Unsafe (will panic):**

```go
var analytics *Analytics
analytics.MessagesTotal++  // ❌ PANIC!
```

**Safe:**

```go
var analytics *Analytics
if analytics != nil {
    analytics.MessagesTotal++  // ✓ Safe
}
```

### Checking Nil in Your Code

```go
func analyzeMessage(a *Analytics, m Message) {
    if a == nil {
        return  // Can't update nil pointer
    }
    
    if m.Success {
        a.MessagesSucceeded++
    } else {
        a.MessagesFailed++
    }
    a.MessagesTotal++
}
```

### Key Takeaways

1. **Nil pointer** = pointer that doesn't point to anything
2. **Dereferencing nil** = runtime panic (crash)
3. **Always check** `if ptr != nil` before using
4. **Check early** at the start of functions
5. **Return early** if nil to avoid crashes

### Quick Reference

| Action                     | Code                      | Safe?          |
| -------------------------- | ------------------------- | -------------- |
| Declare pointer            | `var p *int`              | ✓ (just nil)   |
| Check if nil               | `if p == nil`             | ✓ Safe check   |
| Dereference without check  | `*p = 5`                  | ❌ Panic if nil |
| Dereference with check     | `if p != nil { *p = 5 }`  | ✓ Safe         |
| Access field without check | `p.field`                 | ❌ Panic if nil |
| Access field with check    | `if p != nil { p.field }` | ✓ Safe         |

**Rule:** When working with pointers, always ask "Could this be nil?" If yes, check before using.

### Assignment

Let's make our profanity checker safe. Update the `removeProfanity` function. If `message` is `nil`, `return` early to avoid a [panic](https://gobyexample.com/panic). After all, there are no bad words to remove.

#### Solution

{% code title="main.go" %}

```go
package main

import (
	"strings"
)

func removeProfanity(message *string) {
	if message == nil {
		return
	}
	messageVal := *message
	messageVal = strings.ReplaceAll(messageVal, "fubb", "****")
	messageVal = strings.ReplaceAll(messageVal, "shiz", "****")
	messageVal = strings.ReplaceAll(messageVal, "witch", "*****")
	*message = messageVal
}
```

{% endcode %}
