Skip to main content

Comprehensive Go language cheatsheet, written to be something you can actually keep open while coding. It includes core syntax, language rules, standard patterns, and essential "Go-isms".

---
title: Go (Golang) Language Cheatsheet
subtitle: Comprehensive Go language cheatsheet, written to be something you can actually keep open while coding.
author: Jon LaBelle
date: February 27, 2026
source: https://jonlabelle.com/snippets/view/markdown/go-golang-language-cheatsheet
---

Comprehensive Go language cheatsheet, written to be something you can actually keep open while coding.

It includes core syntax, language rules, standard patterns, and essential "Go-isms".

---

## Table of Contents

1. [Program Structure](#program-structure)
2. [Building & Running](#building--running)
3. [Variables & Constants](#variables--constants)
4. [Primitive Types](#primitive-types)
5. [Type Conversions & Assertions](#type-conversions--assertions)
6. [Operators](#operators)
7. [Strings](#strings)
8. [Control Flow](#control-flow)
9. [Functions](#functions)
10. [Closures](#closures)
11. [Pointers](#pointers)
12. [Arrays](#arrays)
13. [Slices](#slices)
14. [Maps](#maps)
15. [Structs](#structs)
16. [Embedding](#embedding)
17. [Methods & Receivers](#methods--receivers)
18. [Interfaces](#interfaces)
19. [Generics](#generics)
20. [Error Handling](#error-handling)
21. [`defer`, `panic`, `recover`](#defer-panic-recover)
22. [`init` Functions](#init-functions)
23. [Concurrency](#concurrency)
24. [Channels](#channels)
25. [Context](#context)
26. [Packages & Modules](#packages--modules)
27. [Visibility & Naming](#visibility--naming)
28. [Memory & Zero Values](#memory--zero-values)
29. [Testing](#testing)
30. [Common Standard Library](#common-standard-library)
31. [Example Applications](#example-applications)
32. [Common Gotchas](#common-gotchas)
33. [Idiomatic Go Rules](#idiomatic-go-rules)

---

## Program Structure

```go
package main // every executable must be in package main

import (
    "fmt"  // grouped imports (preferred over multiple import statements)
    "os"
)

func main() { // entry point — takes no args, returns nothing
    fmt.Println("Hello, Go")
    os.Exit(0) // explicit exit code (rarely needed; 0 is default)
}
```

- Every executable has `package main` and `func main()`
- Imports are explicit — unused imports are a **compile error**
- Use `_` to import for side effects only: `import _ "net/http/pprof"`

---

## Building & Running

### Run Without Compiling

```bash
go run main.go                # compile + run in one step (no binary produced)
go run .                      # run the package in the current directory
```

### Build a Binary

```bash
go build                      # builds binary named after module (in current dir)
go build -o myapp             # specify output binary name
go build -o bin/myapp ./cmd/server  # build a specific sub-package
```

### Install Globally

```bash
go install                    # builds and places binary in $GOPATH/bin (or $GOBIN)
go install example.com/tool@latest  # install a remote tool
```

### Cross-Compilation

```bash
# Go cross-compiles with just two env vars — no extra toolchain needed
GOOS=linux GOARCH=amd64 go build -o myapp-linux
GOOS=darwin GOARCH=arm64 go build -o myapp-mac
GOOS=windows GOARCH=amd64 go build -o myapp.exe
```

Common `GOOS`/`GOARCH` combos:

| `GOOS`    | `GOARCH` | Target                         |
| --------- | -------- | ------------------------------ |
| `linux`   | `amd64`  | Linux x86-64                   |
| `linux`   | `arm64`  | Linux ARM (e.g., AWS Graviton) |
| `darwin`  | `amd64`  | macOS Intel                    |
| `darwin`  | `arm64`  | macOS Apple Silicon            |
| `windows` | `amd64`  | Windows x86-64                 |

### Build Tags

```go
//go:build linux
// +build linux    // old syntax (pre-1.17), still works

package mypackage
```

```bash
go build -tags "integration"    # include files with //go:build integration
```

### Embed Version Info at Build Time

```go
// main.go
var version = "dev" // default value
```

```bash
go build -ldflags "-X main.version=1.2.3" -o myapp
# sets the version variable at compile time
```

### Build Flags Summary

| Flag                        | Purpose                                 |
| --------------------------- | --------------------------------------- |
| `-o name`                   | output binary name                      |
| `-v`                        | verbose (print packages being compiled) |
| `-race`                     | enable race detector                    |
| `-ldflags "-s -w"`          | strip debug info (smaller binary)       |
| `-ldflags "-X pkg.Var=val"` | inject string variable at build time    |
| `-tags "tag1 tag2"`         | enable build tags                       |
| `-trimpath`                 | remove local file paths from binary     |

### What `go build` Produces

- A **single static binary** — no runtime dependencies, no VM, no interpreter
- Includes the Go runtime and garbage collector
- Typical binary size: 5-15 MB (use `-ldflags "-s -w"` to shrink)
- Binary is ready to deploy — just copy it to the target machine

---

## Variables & Constants

### Short Declaration (inside functions only)

```go
x := 10             // type inferred as int
name := "Jon"        // type inferred as string
a, b := 1, "hello"  // multiple assignment
```

### Explicit Declaration (anywhere)

```go
var x int = 10   // explicit type + value
var y = 20       // type inferred
var z int        // zero-initialized (z == 0)
var a, b int     // multiple vars, both zero
```

### Package-Level Variables

```go
var Version = "1.0.0" // exported (uppercase)
var debug = false      // unexported (lowercase)
```

### Constants

```go
const Pi = 3.14159    // untyped constant — higher precision until assigned
const Timeout = 5 * time.Second

const (
    StatusOK    = 200
    StatusNotOK = 400
)
```

### Iota (auto-incrementing constant generator)

```go
const (
    Read    = 1 << iota // 1
    Write               // 2
    Execute             // 4
)

const (
    Sunday = iota // 0
    Monday        // 1
    Tuesday       // 2
)
```

- Constants must be compile-time evaluable
- No `const` for slices, maps, or structs
- Untyped constants adapt to context: `const x = 5` works as int, float64, etc.

---

## Primitive Types

### Numeric

```go
int, int8, int16, int32, int64       // signed integers
uint, uint8, uint16, uint32, uint64  // unsigned integers
float32, float64                     // IEEE 754 floats
complex64, complex128                // complex numbers
uintptr                              // pointer-sized unsigned int (unsafe)
```

> `int` and `uint` are platform-dependent: 32-bit on 32-bit systems, 64-bit on 64-bit.

### Aliases

```go
byte // alias for uint8 — used for raw data
rune // alias for int32 — represents a Unicode code point
```

### Other

```go
bool   // true or false (zero value: false)
string // immutable UTF-8 byte sequence (zero value: "")
```

### Type Sizes

| Type      | Size    | Range                       |
| --------- | ------- | --------------------------- |
| `int8`    | 1 byte  | -128 to 127                 |
| `int16`   | 2 bytes | -32,768 to 32,767           |
| `int32`   | 4 bytes | -2.1B to 2.1B               |
| `int64`   | 8 bytes | ±9.2 quintillion            |
| `float32` | 4 bytes | ~7 decimal digits precision |
| `float64` | 8 bytes | ~15 decimal digits          |

---

## Type Conversions & Assertions

### Type Conversion (between compatible types)

```go
i := 42
f := float64(i)    // int → float64
u := uint(f)       // float64 → uint
s := string(rune(65)) // int → rune → string ("A")
```

> Go has **no implicit conversions**. Even `int32` → `int64` requires explicit cast.

### String ↔ Number

```go
import "strconv"

s := strconv.Itoa(42)          // int → string: "42"
i, err := strconv.Atoi("42")   // string → int: 42
f, err := strconv.ParseFloat("3.14", 64) // string → float64
```

### Type Assertion (interfaces only)

```go
var x any = "hello"

s := x.(string)        // panics if x is not a string
s, ok := x.(string)    // safe — ok is false if wrong type

if s, ok := x.(string); ok {
    fmt.Println(s) // use s safely
}
```

### Type Switch

```go
switch v := x.(type) {
case int:
    fmt.Println("int:", v)
case string:
    fmt.Println("string:", v)
case nil:
    fmt.Println("nil")
default:
    fmt.Printf("unknown: %T\n", v)
}
```

---

## Operators

### Arithmetic

```go
+  -  *  /  %   // add, subtract, multiply, divide, modulo
++  --           // increment/decrement (statement only, not expression)
```

> `i++` is a statement. You **cannot** write `x = i++`.

### Comparison

```go
==  !=  <  <=  >  >=
```

### Logical

```go
&&  ||  !  // AND, OR, NOT (short-circuit evaluation)
```

### Bitwise

```go
&   // AND
|   // OR
^   // XOR (also unary NOT)
<<  // left shift
>>  // right shift
&^  // AND NOT (bit clear)
```

### Assignment

```go
=  :=  +=  -=  *=  /=  %=  &=  |=  ^=  <<=  >>=
```

- ❌ No ternary operator (`? :`) — use `if/else`
- ❌ No operator overloading

---

## Strings

```go
s := "hello, world"       // double quotes for interpreted strings
r := `raw \n string`       // backticks for raw strings (no escape processing)
```

### Common Operations

```go
len(s)                     // byte length (not rune count!)
utf8.RuneCountInString(s)  // actual character count
s[0]                       // byte at index (not rune!)
s[1:4]                     // substring (byte indices)
s + " suffix"              // concatenation (creates new string)
```

### Iterating

```go
for i, r := range s {     // r is a rune, i is byte index
    fmt.Printf("%d: %c\n", i, r)
}

// to iterate bytes:
for i := 0; i < len(s); i++ {
    fmt.Println(s[i])
}
```

### strings Package

```go
import "strings"

strings.Contains(s, "llo")       // true
strings.HasPrefix(s, "hel")      // true
strings.HasSuffix(s, "rld")      // true
strings.Index(s, "lo")           // 3
strings.ToUpper(s)                // "HELLO, WORLD"
strings.ToLower(s)                // "hello, world"
strings.TrimSpace("  hi  ")      // "hi"
strings.Split("a,b,c", ",")      // []string{"a", "b", "c"}
strings.Join([]string{"a","b"}, "-") // "a-b"
strings.ReplaceAll(s, "l", "L")  // "heLLo, worLd"
strings.Repeat("ha", 3)          // "hahaha"
```

### Efficient String Building

```go
var b strings.Builder   // use Builder for many concatenations
b.WriteString("hello")
b.WriteString(" world")
s := b.String()         // "hello world"
```

> Strings are **immutable**. Every concatenation allocates a new string. Use `strings.Builder` or `bytes.Buffer` in loops.

---

## Control Flow

### If / Else

```go
if x > 0 {
    // positive
} else if x == 0 {
    // zero
} else {
    // negative
}
```

With initializer (scoped to if/else block):

```go
if v, err := strconv.Atoi(s); err == nil {
    fmt.Println(v)
} else {
    fmt.Println("error:", err)
}
// v and err are NOT accessible here
```

---

### For (the only loop keyword)

```go
// classic C-style
for i := 0; i < 10; i++ {}

// while-style
for condition {}

// infinite loop
for {
    break    // exit loop
    continue // skip to next iteration
}
```

### Range (iterate over collections)

```go
for i, v := range slice {}    // index + value
for _, v := range slice {}    // value only
for i := range slice {}       // index only

for k, v := range aMap {}     // key + value
for k := range aMap {}        // key only

for i, r := range "hello" {} // i = byte index, r = rune
for range 5 {}                // Go 1.22+: repeat 5 times
```

### Labels & Break/Continue

```go
outer:
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            if j == 1 {
                continue outer // skip to next outer iteration
            }
            if i == 2 {
                break outer    // exit both loops
            }
        }
    }
```

---

### Switch

```go
switch x {
case 1:
    fmt.Println("one")   // no fallthrough by default (unlike C)
case 2, 3:
    fmt.Println("two or three")   // multiple values
    fallthrough           // explicit fallthrough to next case
case 4:
    fmt.Println("falls here from 2 or 3")
default:
    fmt.Println("other")
}
```

Expression-less switch (cleaner than if/else chains):

```go
switch {
case x > 100:
    fmt.Println("big")
case x > 10:
    fmt.Println("medium")
default:
    fmt.Println("small")
}
```

---

## Functions

```go
func add(a int, b int) int {    // basic function
    return a + b
}

func add(a, b int) int {        // shorthand when types match
    return a + b
}
```

### Multiple Returns

```go
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

result, err := divide(10, 3)
```

### Named Returns (use judiciously — can hurt readability)

```go
func divide(a, b float64) (result float64, err error) {
    if b == 0 {
        err = errors.New("division by zero")
        return // "naked return" — returns named values
    }
    result = a / b
    return
}
```

### Variadic Functions

```go
func sum(nums ...int) int {   // nums is []int
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

sum(1, 2, 3)
sum(nums...)    // spread a slice
```

### First-Class Functions

```go
var fn func(int) int            // function variable
fn = func(x int) int { return x * 2 }
fmt.Println(fn(5))              // 10

// as parameter
func apply(f func(int) int, x int) int {
    return f(x)
}
```

---

## Closures

```go
func counter() func() int {
    n := 0                      // captured by the closure
    return func() int {
        n++                     // mutates the captured variable
        return n
    }
}

c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
```

⚠️ **Loop variable capture pitfall:**

```go
// WRONG — all goroutines share the same `i`
for i := 0; i < 5; i++ {
    go func() { fmt.Println(i) }() // likely prints "5" five times
}

// CORRECT (Go <1.22) — pass as argument
for i := 0; i < 5; i++ {
    go func(i int) { fmt.Println(i) }(i)
}

// Go 1.22+ — loop vars are per-iteration by default (fixed!)
```

---

## Pointers

```go
x := 42
p := &x         // p is *int, points to x
fmt.Println(*p)  // 42 — dereference to read
*p = 100         // modify x through the pointer
fmt.Println(x)   // 100
```

### Why Use Pointers?

```go
// 1. Mutate the caller's data
func double(n *int) {
    *n *= 2
}

x := 5
double(&x)
fmt.Println(x) // 10

// 2. Avoid copying large structs
func process(u *User) { /* works on original, no copy */ }

// 3. Signal "optional" values (nil means absent)
func find(id int) *User {
    if id == 0 {
        return nil
    }
    return &User{ID: id}
}
```

- ❌ No pointer arithmetic (use `unsafe` package if you must — you shouldn't)
- `new(T)` allocates zeroed `T` and returns `*T`: `p := new(int) // *int, value 0`
- Pointers to local variables are fine — Go's escape analysis handles it

---

## Arrays

```go
var a [3]int             // [0, 0, 0] — zero-valued
b := [3]int{1, 2, 3}    // [1, 2, 3]
c := [...]int{1, 2, 3}  // compiler counts: [3]int
```

- **Fixed size** — size is part of the type: `[3]int ≠ [4]int`
- **Value types** — assignment and function args copy the entire array
- Rarely used directly; **use slices instead**

---

## Slices

A slice is a reference to a contiguous segment of an array: `(pointer, length, capacity)`.

```go
s := []int{1, 2, 3}     // slice literal (no size = slice, not array)
s := make([]int, 5)      // len=5, cap=5, all zeros
s := make([]int, 0, 10)  // len=0, cap=10 (pre-allocate)
```

### Append

```go
s = append(s, 4)         // may allocate a new backing array
s = append(s, 5, 6, 7)   // append multiple
s = append(s, other...)   // append another slice
```

### Slice Expressions

```go
a := []int{0, 1, 2, 3, 4}
b := a[1:3]  // [1, 2] — shares backing array with a
c := a[:3]   // [0, 1, 2]
d := a[2:]   // [2, 3, 4]
e := a[:]    // full slice (shallow copy of header, same backing array)
```

### Full Slice Expression (limit capacity)

```go
b := a[1:3:3] // [1, 2] — cap=2, prevents append from overwriting a's data
```

### Copy

```go
dst := make([]int, len(src))
n := copy(dst, src)   // returns number of elements copied (min of both lens)
```

### Delete Element (order-preserving)

```go
s = append(s[:i], s[i+1:]...) // remove element at index i
s = slices.Delete(s, i, i+1)  // Go 1.21+ (preferred)
```

### Nil vs Empty

```go
var s []int      // nil — len=0, cap=0, s == nil is true
s := []int{}     // empty — len=0, cap=0, s == nil is false
// both work with len(), cap(), append(), range
// json.Marshal: nil → "null", empty → "[]"
```

⚠️ Slices returned by sub-slicing share the same backing array. Mutating one affects the other.

---

## Maps

```go
m := map[string]int{     // map literal
    "a": 1,
    "b": 2,
}
m := make(map[string]int)       // empty map
m := make(map[string]int, 100)  // empty map with capacity hint
```

### Operations

```go
m["key"] = 42              // set
v := m["key"]              // get (returns zero value if missing)
v, ok := m["key"]          // comma-ok idiom — ok is false if missing
delete(m, "key")           // delete (no-op if key absent)
len(m)                     // number of entries
```

### Iteration

```go
for k, v := range m {
    fmt.Println(k, v)      // order is randomized each run!
}
```

### Nil Maps

```go
var m map[string]int       // nil map
_ = m["key"]               // safe — returns zero value
m["key"] = 1               // PANIC — can't write to nil map
```

- **Reference type** — passing to a function shares the underlying data
- **Not thread-safe** — concurrent reads/writes cause a fatal crash. Use `sync.Map` or a `sync.RWMutex`
- Keys must be **comparable** (`==` supported): no slices, maps, or functions as keys

---

## Structs

```go
type User struct {
    ID        int
    Name      string
    Email     string    `json:"email"` // struct tag for JSON field name
    CreatedAt time.Time `json:"created_at,omitempty"` // omit if zero
}
```

### Construction

```go
u := User{ID: 1, Name: "Jon"}          // named fields (preferred)
u := User{1, "Jon", "", time.Time{}}   // positional (fragile, avoid)
p := &User{ID: 1}                      // pointer to struct
u := new(User)                         // *User, zero-valued
```

### Access

```go
u.Name                     // direct field access
p.Name                     // auto-dereferenced (same as (*p).Name)
```

### Anonymous Structs (useful for tests, one-off data)

```go
point := struct {
    X, Y int
}{X: 1, Y: 2}
```

### Struct Comparison

```go
// Structs are comparable if all fields are comparable
a := User{ID: 1, Name: "Jon"}
b := User{ID: 1, Name: "Jon"}
fmt.Println(a == b) // true
```

---

## Embedding

Go uses **composition** instead of inheritance.

```go
type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return a.Name + " speaks"
}

type Dog struct {
    Animal          // embedded — Dog "inherits" Animal's fields and methods
    Breed string
}

d := Dog{
    Animal: Animal{Name: "Rex"},
    Breed:  "Labrador",
}
fmt.Println(d.Name)    // "Rex" — promoted field
fmt.Println(d.Speak()) // "Rex speaks" — promoted method
```

- Embedding is **not inheritance** — there is no polymorphism between Dog and Animal
- Outer type can **override** promoted methods by defining its own
- You can embed **interfaces** too (useful for partial implementation / decoration)

---

## Methods & Receivers

```go
// Value receiver — operates on a copy
func (u User) FullName() string {
    return u.Name
}

// Pointer receiver — can mutate the original
func (u *User) SetName(n string) {
    u.Name = n
}
```

### Rules

| Use pointer receiver when...                             | Use value receiver when...         |
| -------------------------------------------------------- | ---------------------------------- |
| Method modifies the receiver                             | Method only reads the receiver     |
| Struct is large (avoids copy)                            | Struct is small (e.g., Point{X,Y}) |
| **Consistency** — if any method uses pointer, all should | Type is immutable by design        |

```go
// Go auto-dereferences — you can call pointer methods on values and vice versa
u := User{Name: "Jon"}
u.SetName("Jane")     // compiler takes &u automatically
```

- Methods can only be defined on types in the **same package**
- You **cannot** define methods on built-in types directly (use a named type)

---

## Interfaces

```go
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// Interface composition
type ReadWriter interface {
    Reader
    Writer
}
```

### Implicit Implementation

No `implements` keyword — if a type has the required methods, it satisfies the interface.

```go
type MyReader struct{}

func (MyReader) Read(p []byte) (int, error) {
    return 0, io.EOF
}

var r Reader = MyReader{} // works — MyReader satisfies Reader
```

### Empty Interface

```go
var x any          // any == interface{} (alias since Go 1.18)
x = 42
x = "hello"
x = []int{1, 2}   // anything can be assigned
```

### Interface Best Practices

- Keep interfaces **small** (1-2 methods)
- Define interfaces where they're **consumed**, not where they're implemented
- "Accept interfaces, return structs"
- `io.Reader`, `io.Writer`, `fmt.Stringer`, `error` — learn these well

### Nil Interface Gotcha

```go
var p *User = nil
var i any = p
fmt.Println(i == nil) // false! interface holds (*User, nil), not nil itself
```

---

## Generics

```go
func Map[T any, U any](s []T, f func(T) U) []U {
    result := make([]U, len(s))
    for i, v := range s {
        result[i] = f(v)
    }
    return result
}

// usage
doubled := Map([]int{1, 2, 3}, func(x int) int { return x * 2 })
```

### Constraints

```go
type Number interface {
    ~int | ~int32 | ~int64 | ~float32 | ~float64
    // ~ means underlying type (allows named types like `type Age int`)
}

func Sum[T Number](nums []T) T {
    var total T
    for _, n := range nums {
        total += n
    }
    return total
}
```

### Built-in Constraints

```go
any         // no constraint (== interface{})
comparable  // supports == and != (usable as map keys)
```

### Generic Types

```go
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[T]) Pop() (T, bool) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    v := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return v, true
}
```

---

## Error Handling

### The Pattern

```go
result, err := doSomething()
if err != nil {
    return fmt.Errorf("doSomething failed: %w", err) // wrap with context
}
```

### Creating Errors

```go
// Simple
err := errors.New("something went wrong")

// Formatted
err := fmt.Errorf("user %d not found", id)

// Wrapped (preserves chain for inspection)
err := fmt.Errorf("query failed: %w", originalErr)
```

### Custom Error Types

```go
type NotFoundError struct {
    ID   int
    Type string
}

func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s %d not found", e.Type, e.ID)
}

// Return it
return nil, &NotFoundError{ID: id, Type: "user"}
```

### Inspecting Errors

```go
// Is — checks if any error in the chain matches a target value
if errors.Is(err, os.ErrNotExist) {
    fmt.Println("file not found")
}

// As — checks if any error in the chain matches a target type
var nfe *NotFoundError
if errors.As(err, &nfe) {
    fmt.Println("missing:", nfe.Type, nfe.ID)
}
```

### Sentinel Errors

```go
var ErrNotFound = errors.New("not found")  // package-level, exported
var errInternal = errors.New("internal")    // package-level, unexported
```

> **Never** compare errors with `==` unless they're sentinel values. Use `errors.Is()`.

---

## `defer`, `panic`, `recover`

### defer

```go
f, err := os.Open("file.txt")
if err != nil {
    return err
}
defer f.Close() // guaranteed to run when function returns

// Multiple defers execute in LIFO order (stack)
defer fmt.Println("first")
defer fmt.Println("second") // prints "second" then "first"
```

> Deferred calls' **arguments are evaluated immediately**, but the call happens later:
>
> ```go
> x := 1
> defer fmt.Println(x) // prints 1, not 2
> x = 2
> ```

### panic

```go
panic("something went catastrophically wrong")
panic(fmt.Sprintf("unexpected type: %T", v))
```

- Unwinds the call stack, running deferred functions
- Crashes the program if not recovered
- **Don't use for normal error handling** — only for truly unrecoverable situations

### recover

```go
func safeCall() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recovered:", r)
        }
    }()

    panic("oh no")
}
```

- `recover()` only works inside a **deferred function**
- Returns `nil` if no panic is in progress
- Common in libraries and HTTP handlers to prevent one bad request from crashing the server

---

## `init` Functions

```go
package mypackage

func init() {
    // runs automatically when package is imported
    // before main() executes
    // used for: registering drivers, setting defaults, validation
}
```

- Each file can have **multiple** `init()` functions
- Execution order: package-level vars → `init()` → `main()`
- Avoid complex logic in `init()` — it makes testing and debugging harder

---

## Concurrency

### Goroutines

```go
go doWork()                                       // fire and forget

go func() {                                       // anonymous goroutine
    fmt.Println("running concurrently")
}()
```

- Goroutines are **multiplexed** onto OS threads by Go's runtime scheduler
- They start with a ~8KB stack that grows dynamically
- Spawning thousands (even millions) is normal

### sync.WaitGroup (wait for goroutines to finish)

```go
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)                 // increment before launching
    go func(id int) {
        defer wg.Done()       // decrement when done
        fmt.Println("worker", id)
    }(i)
}

wg.Wait()                     // block until counter is zero
```

### sync.Mutex (protect shared data)

```go
var mu sync.Mutex
var count int

func increment() {
    mu.Lock()
    defer mu.Unlock()
    count++
}
```

### sync.RWMutex (multiple readers, single writer)

```go
var mu sync.RWMutex

func read() int {
    mu.RLock()
    defer mu.RUnlock()
    return count
}
```

### sync.Once (run exactly once, even across goroutines)

```go
var once sync.Once
var instance *DB

func GetDB() *DB {
    once.Do(func() {
        instance = connectDB()
    })
    return instance
}
```

⚠️ **Concurrency rules:**

- Don't communicate by sharing memory; share memory by communicating (channels)
- If you share memory, protect it with a mutex
- No automatic cancellation or cleanup for goroutines — you must handle it

---

## Channels

Channels are typed conduits for passing data between goroutines.

```go
ch := make(chan int)       // unbuffered — send blocks until receiver is ready
ch := make(chan int, 10)   // buffered — send blocks only when buffer is full
```

### Send / Receive

```go
ch <- 42        // send (blocks if unbuffered and no receiver)
v := <-ch       // receive (blocks until a value is sent)
v, ok := <-ch   // ok is false if channel is closed and empty
```

### Close

```go
close(ch)       // signal that no more values will be sent
// receiving from closed channel returns zero value immediately
// sending to closed channel panics!
```

### Range Over Channel

```go
for v := range ch {        // loops until channel is closed
    fmt.Println(v)
}
```

### Directional Channels (used in function signatures)

```go
func produce(ch chan<- int) { ch <- 1 }  // send-only
func consume(ch <-chan int) { <-ch }     // receive-only
```

### Select (multiplexing channels)

```go
select {
case v := <-ch1:
    fmt.Println("from ch1:", v)
case ch2 <- 42:
    fmt.Println("sent to ch2")
case <-time.After(1 * time.Second):
    fmt.Println("timeout")
default:
    fmt.Println("no channel ready") // non-blocking if default present
}
```

### Common Patterns

```go
// Done channel (signaling completion)
done := make(chan struct{})
go func() {
    defer close(done)
    doWork()
}()
<-done // wait for completion

// Fan-out: launch multiple goroutines reading from one channel
// Fan-in: merge multiple channels into one
// Pipeline: chain of stages connected by channels
```

---

## Context

Used for cancellation, deadlines, and request-scoped values across API boundaries.

```go
import "context"

// Create contexts
ctx := context.Background()                     // root context (top-level)
ctx := context.TODO()                           // placeholder when unsure

ctx, cancel := context.WithCancel(parent)       // manual cancellation
ctx, cancel := context.WithTimeout(parent, 5*time.Second) // auto-cancel after timeout
ctx, cancel := context.WithDeadline(parent, deadline)     // auto-cancel at deadline

defer cancel() // ALWAYS call cancel to release resources
```

### Checking for Cancellation

```go
select {
case <-ctx.Done():
    fmt.Println("cancelled:", ctx.Err()) // context.Canceled or DeadlineExceeded
    return
case result := <-ch:
    fmt.Println(result)
}
```

### Passing Context

```go
// Always pass as the first parameter, named ctx
func FetchData(ctx context.Context, url string) ([]byte, error) {
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    // ...
}
```

> **Never** store context in a struct. Pass it explicitly.

---

## Packages & Modules

### Initialize a Module

```bash
go mod init example.com/myapp    # creates go.mod
go mod tidy                       # add missing / remove unused dependencies
go get example.com/pkg@v1.2.3    # add a dependency
go get -u ./...                   # update all dependencies
```

### Import

```go
import "fmt"                     // standard library
import "example.com/myapp/util"  // local package
import m "math"                  // aliased import (m.Sqrt)
import . "math"                  // dot import — use Sqrt directly (avoid)
import _ "image/png"             // side-effect import (registers PNG decoder)
```

### Project Layout (common convention)

```
myapp/
├── go.mod
├── go.sum            # checksums (not a lockfile — auto-generated)
├── main.go           # package main
├── internal/         # private packages (not importable outside module)
│   └── db/
├── pkg/              # public library packages (optional convention)
│   └── util/
└── cmd/              # multiple binaries
    ├── server/
    └── cli/
```

- One module per `go.mod` = many packages
- Package name = directory name (by convention)
- `internal/` enforced by the compiler — cannot be imported from outside

---

## Visibility & Naming

```go
func PublicFunc()    // exported — accessible from other packages
func privateFunc()   // unexported — package-private

type PublicType struct {
    ExportedField   int    // accessible externally
    unexportedField int    // only within this package
}
```

### Naming Conventions

| Thing                | Convention                       | Example                  |
| -------------------- | -------------------------------- | ------------------------ |
| Package              | short, lowercase, no underscores | `http`, `strconv`        |
| Interface (1 method) | method name + "er"               | `Reader`, `Stringer`     |
| Acronyms             | all caps                         | `URL`, `HTTP`, `ID`      |
| Getters              | no `Get` prefix                  | `Name()` not `GetName()` |
| Local vars           | short                            | `i`, `n`, `err`, `ctx`   |

---

## Memory & Zero Values

Every type has a zero value — allocated memory is always initialized.

| Type        | Zero Value        |
| ----------- | ----------------- |
| `int`       | `0`               |
| `float64`   | `0.0`             |
| `bool`      | `false`           |
| `string`    | `""`              |
| `pointer`   | `nil`             |
| `slice`     | `nil`             |
| `map`       | `nil`             |
| `channel`   | `nil`             |
| `interface` | `nil`             |
| `struct`    | all fields zeroed |
| `func`      | `nil`             |

### Allocation

```go
p := new(User)              // allocates zeroed User, returns *User
s := make([]int, 10)        // make is for slices, maps, channels only
m := make(map[string]int)
ch := make(chan int, 5)
```

> **Stack vs heap**: Go's escape analysis decides. If a value outlives the function (e.g., returned pointer), it's heap-allocated. You rarely need to think about this.

### Nil Safety

```go
var s []int
len(s)          // 0 (safe)
append(s, 1)    // works (safe)
for range s {}  // works (safe)
s[0]            // PANIC — index out of range

var m map[string]int
m["key"]        // returns 0 (safe read)
m["key"] = 1    // PANIC — assignment to nil map
```

---

## Testing

### Basic Test

```go
// file: math_test.go (must end in _test.go)
package math

import "testing"

func TestAdd(t *testing.T) {         // must start with Test
    got := Add(2, 3)
    want := 5
    if got != want {
        t.Errorf("Add(2,3) = %d, want %d", got, want)
    }
}
```

### Table-Driven Tests (idiomatic)

```go
func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive", 1, 2, 3},
        {"zero", 0, 0, 0},
        {"negative", -1, -2, -3},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := Add(tt.a, tt.b); got != tt.expected {
                t.Errorf("got %d, want %d", got, tt.expected)
            }
        })
    }
}
```

### Benchmarks

```go
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(1, 2)
    }
}
// run: go test -bench=.
```

### Running Tests

```bash
go test ./...              # all packages
go test -v ./...           # verbose
go test -run TestAdd       # specific test
go test -cover             # coverage
go test -race              # race detector (essential for concurrent code)
```

---

## Common Standard Library

```go
// Formatting
fmt.Println("hello")                   // print with newline
fmt.Printf("name: %s, age: %d\n", n, a)  // formatted
fmt.Sprintf("id-%d", 42)              // returns string
fmt.Fprintf(w, "hello")               // write to io.Writer

// I/O
io.Copy(dst, src)                     // copy reader to writer
io.ReadAll(r)                         // read all bytes (Go 1.16+)
os.ReadFile("path")                   // read file to []byte (Go 1.16+)
os.WriteFile("path", data, 0644)      // write []byte to file

// Time
time.Now()                             // current time
time.Since(start)                      // duration since start
time.Sleep(100 * time.Millisecond)
t.Format("2006-01-02 15:04:05")       // Go uses this specific reference time!
time.Parse("2006-01-02", "2025-02-01")

// JSON
json.Marshal(v)                        // struct → []byte
json.Unmarshal(data, &v)               // []byte → struct
json.NewEncoder(w).Encode(v)           // stream to writer
json.NewDecoder(r).Decode(&v)          // stream from reader

// HTTP
http.Get("https://example.com")
http.ListenAndServe(":8080", handler)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello")
})

// Sorting
sort.Ints(s)                           // sort []int in place
sort.Strings(s)                        // sort []string in place
slices.Sort(s)                         // Go 1.21+ generic sort
slices.SortFunc(s, func(a, b int) int { return a - b })

// Logging
log.Println("info message")
log.Fatalf("fatal: %v", err)           // logs and calls os.Exit(1)
slog.Info("msg", "key", "value")       // structured logging (Go 1.21+)
```

### Printf Verbs Cheatsheet

| Verb   | Description              | Example Output    |
| ------ | ------------------------ | ----------------- |
| `%v`   | default format           | `{1 Jon}`         |
| `%+v`  | with field names         | `{ID:1 Name:Jon}` |
| `%#v`  | Go syntax                | `User{ID:1}`      |
| `%T`   | type                     | `main.User`       |
| `%d`   | decimal integer          | `42`              |
| `%x`   | hex                      | `2a`              |
| `%f`   | float                    | `3.140000`        |
| `%.2f` | float (2 decimal)        | `3.14`            |
| `%s`   | string                   | `hello`           |
| `%q`   | quoted string            | `"hello"`         |
| `%p`   | pointer                  | `0xc0000b4008`    |
| `%w`   | wrap error (Errorf only) | —                 |

---

## Example Applications

### CLI Tool: Word Counter

A simple CLI that counts words, lines, and characters from stdin or files.

```go
// main.go
package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func count(scanner *bufio.Scanner) (lines, words, chars int) {
    for scanner.Scan() {
        line := scanner.Text()
        lines++
        words += len(strings.Fields(line))
        chars += len(line) + 1 // +1 for newline
    }
    return
}

func main() {
    var scanner *bufio.Scanner

    if len(os.Args) > 1 {
        // read from file
        f, err := os.Open(os.Args[1])
        if err != nil {
            fmt.Fprintf(os.Stderr, "error: %v\n", err)
            os.Exit(1)
        }
        defer f.Close()
        scanner = bufio.NewScanner(f)
    } else {
        // read from stdin
        scanner = bufio.NewScanner(os.Stdin)
    }

    lines, words, chars := count(scanner)
    fmt.Printf("%8d %8d %8d\n", lines, words, chars)
}
```

```bash
# build and run
go build -o wc .
echo "hello world" | ./wc          #        1        2       12
./wc main.go                       # counts lines/words/chars in main.go
```

---

### HTTP Server: JSON API

A minimal REST API with health check, JSON responses, and graceful shutdown.

```go
// main.go
package main

import (
    "context"
    "encoding/json"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"
)

type StatusResponse struct {
    Status    string `json:"status"`
    Timestamp string `json:"timestamp"`
}

type Message struct {
    ID   int    `json:"id"`
    Text string `json:"text"`
}

// respondJSON writes a JSON response with the given status code
func respondJSON(w http.ResponseWriter, status int, data any) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

func main() {
    mux := http.NewServeMux()

    // health check
    mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
        respondJSON(w, http.StatusOK, StatusResponse{
            Status:    "ok",
            Timestamp: time.Now().UTC().Format(time.RFC3339),
        })
    })

    // list messages
    messages := []Message{
        {ID: 1, Text: "Hello, Go!"},
        {ID: 2, Text: "Concurrency is not parallelism."},
    }

    mux.HandleFunc("GET /messages", func(w http.ResponseWriter, r *http.Request) {
        respondJSON(w, http.StatusOK, messages)
    })

    // create message
    mux.HandleFunc("POST /messages", func(w http.ResponseWriter, r *http.Request) {
        var msg Message
        if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
            respondJSON(w, http.StatusBadRequest, map[string]string{
                "error": "invalid JSON",
            })
            return
        }
        msg.ID = len(messages) + 1
        messages = append(messages, msg)
        respondJSON(w, http.StatusCreated, msg)
    })

    // configure server
    srv := &http.Server{
        Addr:         ":8080",
        Handler:      mux,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    // start server in a goroutine
    go func() {
        log.Printf("listening on %s", srv.Addr)
        if err := srv.ListenAndServe(); err != http.ErrServerClosed {
            log.Fatalf("server error: %v", err)
        }
    }()

    // graceful shutdown: wait for interrupt signal
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)
    <-quit // block until SIGINT

    log.Println("shutting down...")
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    if err := srv.Shutdown(ctx); err != nil {
        log.Fatalf("shutdown error: %v", err)
    }
    log.Println("server stopped")
}
```

```bash
# build and run
go build -o api-server .
./api-server

# test endpoints (in another terminal)
curl http://localhost:8080/health
curl http://localhost:8080/messages
curl -X POST http://localhost:8080/messages \
  -H "Content-Type: application/json" \
  -d '{"text": "A new message"}'
```

> **Note:** The `"GET /messages"` pattern syntax requires **Go 1.22+**. For older versions, use `mux.HandleFunc("/messages", handler)` and check `r.Method` manually.

---

### Concurrent Pipeline: File Processor

Demonstrates goroutines, channels, WaitGroup, and the pipeline pattern.

```go
// main.go
package main

import (
    "crypto/sha256"
    "fmt"
    "os"
    "path/filepath"
    "sync"
)

// FileHash holds a file path and its SHA-256 checksum
type FileHash struct {
    Path string
    Hash string
    Err  error
}

// walk sends file paths on a channel (producer)
func walk(root string) <-chan string {
    paths := make(chan string)
    go func() {
        defer close(paths) // close signals no more files
        filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
            if err != nil || info.IsDir() {
                return nil
            }
            paths <- path
            return nil
        })
    }()
    return paths
}

// hash reads files and computes checksums (worker)
func hash(paths <-chan string, results chan<- FileHash) {
    for path := range paths {
        data, err := os.ReadFile(path)
        if err != nil {
            results <- FileHash{Path: path, Err: err}
            continue
        }
        sum := sha256.Sum256(data)
        results <- FileHash{Path: path, Hash: fmt.Sprintf("%x", sum)}
    }
}

func main() {
    root := "."
    if len(os.Args) > 1 {
        root = os.Args[1]
    }

    // stage 1: walk directory tree
    paths := walk(root)

    // stage 2: fan-out to N workers
    results := make(chan FileHash)
    var wg sync.WaitGroup
    numWorkers := 4

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            hash(paths, results)
        }()
    }

    // close results channel when all workers are done
    go func() {
        wg.Wait()
        close(results)
    }()

    // stage 3: collect results
    for r := range results {
        if r.Err != nil {
            fmt.Fprintf(os.Stderr, "error: %s: %v\n", r.Path, r.Err)
            continue
        }
        fmt.Printf("%s  %s\n", r.Hash[:16], r.Path)
    }
}
```

```bash
go build -o hasher .
./hasher .              # hash all files in current directory
./hasher /path/to/dir   # hash all files in a specific directory
```

---

## Common Gotchas

| Gotcha                        | Explanation                                                             |
| ----------------------------- | ----------------------------------------------------------------------- |
| `nil` interface ≠ `nil` value | An interface holding a nil pointer is **not** nil itself                |
| Shadowing with `:=`           | Inner scope `:=` creates a new variable, doesn't reassign outer         |
| Map iteration order           | Randomized intentionally — don't depend on it                           |
| Slice sharing                 | Sub-slices share the backing array; use `copy` or full slice expression |
| Nil map write panics          | Must `make()` a map before writing                                      |
| JSON tags are case-sensitive  | `json:"name"` must match exactly                                        |
| Loop var capture (pre-1.22)   | Goroutines in loops capture the variable, not the value                 |
| Goroutine leaks               | Goroutines run until they return — forgotten ones leak memory           |
| `defer` in loops              | Defers don't run until the function exits, not the loop iteration       |
| String indexing returns bytes | `"hello"[0]` is a byte, not a rune                                      |

---

## Idiomatic Go Rules

1. **Prefer clarity over cleverness** — readable code wins
2. **Handle errors immediately** — don't defer error checking
3. **Small interfaces** — 1-2 methods; accept interfaces, return structs
4. **Composition over inheritance** — embed, don't extend
5. **One obvious way to do things** — resist abstraction for its own sake
6. **Don't stutter** — `http.Server` not `http.HTTPServer`
7. **Use `gofmt`** — non-negotiable; format your code
8. **Document exported symbols** — godoc comments start with the name
9. **Make the zero value useful** — `sync.Mutex{}` is ready to use
10. **Don't over-channel** — a mutex is fine when shared state is simpler

---

### TL;DR

Go is:

- **Simple** — small language spec, few keywords
- **Explicit** — no hidden magic, no implicit conversions
- **Fast** — compiled, statically typed, low-overhead concurrency
- **Predictable** — one way to format, one way to handle errors

It rewards discipline and punishes cleverness.