# Go Cheat Sheet

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Reference
Source: https://www.devtutorials.ai/learn/go/reference/go-cheat-sheet

> A lookup page for the whole course: one table per topic, from data types and keywords to files, JSON, goroutines and context.

## What Is This Page?

**Reference page** = a page you look things up in, not one you read from top to bottom.

Nobody remembers every function name.
Programmers remember that a tool exists, then look up how to spell it.

This page gives you:

- one table per topic, in course order
- a Run box wherever seeing beats reading
- the traps worth keeping in your head

**Simple lesson:** remember that a tool exists, and look up the details here.

### A Real-World Example

A developer writing a marks server forgets whether it is `strings.Trim` or `strings.TrimSpace`.
They do not reread a whole tutorial.
They open a reference, find the strings table, and get the answer in seconds.
This page is that reference for everything in the course.

<figure>
  <svg viewBox="0 0 560 190" role="img" aria-label="The topics of the course around the word Go." width="100%">
    <rect x="0" y="0" width="560" height="190" rx="12" fill="#f8fafc" stroke="#e2e8f0" />
    <line x1="190" y1="35" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="20" y="18" width="170" height="34" rx="17" fill="#dbeafe" stroke="#1d4ed8"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="0s" repeatCount="indefinite" /></rect>
    <text x="105" y="40" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#1e3a8a">types and operators</text>
    <line x1="190" y1="95" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="20" y="78" width="170" height="34" rx="17" fill="#dcfce7" stroke="#16a34a"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="1s" repeatCount="indefinite" /></rect>
    <text x="105" y="100" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#14532d">if, switch, for</text>
    <line x1="190" y1="155" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="20" y="138" width="170" height="34" rx="17" fill="#fef3c7" stroke="#d97706"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="2s" repeatCount="indefinite" /></rect>
    <text x="105" y="160" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#78350f">functions, packages</text>
    <line x1="370" y1="35" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="370" y="18" width="170" height="34" rx="17" fill="#ede9fe" stroke="#7c3aed"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="3s" repeatCount="indefinite" /></rect>
    <text x="455" y="40" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#4c1d95">slices, maps, structs</text>
    <line x1="370" y1="95" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="370" y="78" width="170" height="34" rx="17" fill="#fee2e2" stroke="#b91c1c"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="4s" repeatCount="indefinite" /></rect>
    <text x="455" y="100" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#7f1d1d">interfaces, errors</text>
    <line x1="370" y1="155" x2="280" y2="95" stroke="#cbd5e1" strokeWidth="1.5" />
    <rect x="370" y="138" width="170" height="34" rx="17" fill="#ffffff" stroke="#94a3b8"><animate attributeName="stroke-width" values="1;3;1" dur="6s" begin="5s" repeatCount="indefinite" /></rect>
    <text x="455" y="160" textAnchor="middle" fontSize="12" fontFamily="monospace" fill="#0f172a">files, goroutines</text>
    <circle cx="280" cy="95" r="48" fill="#00ADD8" />
    <text x="280" y="101" textAnchor="middle" fontSize="18" fontWeight="bold" fill="#ffffff">Go</text>
  </svg>
  <figcaption>Every topic of the course, on one page.</figcaption>
</figure>

Here is most of the course working together in one program:

```go runnable title="The whole course in one program"
package main

import (
	"errors"
	"fmt"
	"sort"
)

type Result struct {
	Name    string
	Subject string
	Mark    int
}

func (r Result) Grade() string {
	if r.Mark >= 35 {
		return "pass"
	}
	return "fail"
}

var ErrEmpty = errors.New("no results")

func average(rs []Result) (float64, error) {
	if len(rs) == 0 {
		return 0, ErrEmpty
	}
	total := 0
	for _, r := range rs {
		total += r.Mark
	}
	return float64(total) / float64(len(rs)), nil
}

func main() {
	results := []Result{
		{"Asha", "Maths", 78},
		{"Ravi", "Science", 85},
		{"Meera", "English", 30},
	}
	sort.Slice(results, func(i, j int) bool { return results[i].Mark > results[j].Mark })

	count := map[string]int{}
	for _, r := range results {
		fmt.Printf("%-8s%-10s%4d  %s\n", r.Name, r.Subject, r.Mark, r.Grade())
		count[r.Grade()]++
	}

	avg, err := average(results)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%d of %d passed, average %.1f\n", count["pass"], len(results), avg)
}
```

```text
Ravi    Science     85  pass
Asha    Maths       78  pass
Meera   English     30  fail
2 of 3 passed, average 64.3
```

Open this page at the section you need. Each heading matches a lesson you can go back to.

## What You Will Learn

On this page, you will find:

1. The syntax for every topic in the course.
2. Run boxes for the trickiest parts.
3. The traps worth remembering, in one table.

## Data Types

Every basic type from the course, side by side:

| Type | Example | Zero value | Notes |
|---|---|---|---|
| `int` | `78` | `0` | 64 bits on most computers |
| `float64` | `78.5` | `0` | Not exact: `0.1 + 0.2 != 0.3` |
| `string` | `"Asha"` | `""` | Cannot change. `len` counts bytes |
| `bool` | `true` | `false` | Not a number: `if 1 { }` does not build |
| `rune` | `'अ'` | `0` | One Unicode character (`int32`) |
| `byte` | `'A'` | `0` | One byte (`uint8`) |
| `[]int` | `[]int{78, 85}` | `nil` | Slice: grows with `append` |
| `map[string]int` | `map[string]int{"Asha": 78}` | `nil` | Make it before writing |
| `*int` | `&mark` | `nil` | Address of a value |

The ways to make a variable:

| Form | Example | Where |
|---|---|---|
| Full | `var n int = 5` | Anywhere |
| Type from value | `var n = 5` | Anywhere |
| Short | `n := 5` | Inside functions only |
| Many | `a, b := 1, 2` | Inside functions only |
| Constant | `const Pass = 35` | Anywhere |
| Counting constants | `const ( A = iota; B; C )` | Anywhere |
| Ignore a value | `_, err := f()` | Anywhere |

## Keywords

**Keyword** = a word Go keeps for itself. You cannot use it as a name.

Go has only **25** keywords:

| Group | Keywords |
|---|---|
| Program | `package` `import` |
| Declare | `var` `const` `type` `func` |
| Types | `struct` `interface` `map` `chan` |
| Decide | `if` `else` `switch` `case` `default` `fallthrough` `select` |
| Repeat | `for` `range` `break` `continue` `goto` |
| Functions | `return` `defer` `go` |

`int`, `string`, `true`, `nil`, `len` and `append` are **not** keywords. They are built-in names.

## Operators

The operator groups:

| Group | Operators |
|---|---|
| Arithmetic | `+` `-` `*` `/` `%` |
| Increase / decrease | `x++` `x--` (statements, not values) |
| Comparison | `==` `!=` `>` `<` `>=` `<=` |
| Assignment | `=` `:=` `+=` `-=` `*=` `/=` `%=` |
| Logical | `&&` `\|\|` `!` |
| Bitwise | `&` `\|` `^` `&^` `<<` `>>` |
| Address | `&` (address of) `*` (value at) |
| Channel | `<-` |

```go runnable title="Variables, types and operators"
package main

import (
	"fmt"
	"strconv"
)

const passMark = 35

func main() {
	name := "Asha"
	marks, _ := strconv.Atoi("82")
	avg := float64(marks+90) / 2
	fmt.Println(name, marks >= passMark, avg, 17%5, 1<<3)
}
```

```text
Asha true 86 2 8
```

Integer division drops the part after the point: `7 / 2` is `3`. Convert first for `3.5`: `float64(7) / 2`.

## Conditional Statements

| Form | Syntax | Notes |
|---|---|---|
| if / else | `if x > 0 { } else if x < 0 { } else { }` | No brackets around the condition |
| Short statement | `if v, err := f(); err == nil { }` | `v` lives only inside the `if` |
| switch | `switch day { case "Sat", "Sun": ... default: ... }` | No `break` needed |
| No-value switch | `switch { case m >= 90: ... }` | A cleaner `if` chain |
| fallthrough | `case 1: ...; fallthrough` | Runs the next case too. Rarely used |

`{` must stay on the same line as `if`, `else`, `for`, `switch` and `func`. On the next line, Go will not build.

## Loops

Go has **one** loop keyword, `for`, in four shapes:

| Shape | Syntax | Like |
|---|---|---|
| Counting | `for i := 0; i < n; i++ { }` | A normal `for` |
| Condition only | `for x < 100 { }` | `while` in other languages |
| Forever | `for { ... break }` | `while (true)` |
| range | `for i, v := range list { }` | `for each` |

What `range` gives you:

| Over | First value | Second value |
|---|---|---|
| Slice or array | index | element (a copy) |
| String | byte index | `rune` |
| Map | key | value (random order) |
| Channel | value | — |
| Number (Go 1.22+) | `0` to `n-1` | — |

## Functions

| Topic | Syntax | Notes |
|---|---|---|
| Declare | `func add(a, b int) int { return a + b }` | Type after the name |
| Many results | `func div(a, b int) (int, error)` | The Go way to report errors |
| Named results | `func size() (w, h int)` | A bare `return` returns them |
| Variadic | `func sum(nums ...int) int` | Call with `sum(list...)` |
| Function value | `f := func(x int) int { return x * k }` | A closure: remembers `k` |
| defer | `defer f.Close()` | Runs when the function ends, last in first out |

```go runnable title="Conditions, loops and functions"
package main

import "fmt"

func grade(m int) string {
	switch {
	case m >= 90:
		return "A"
	case m >= 75:
		return "B"
	default:
		return "C"
	}
}

func total(marks ...int) int {
	t := 0
	for _, m := range marks {
		t += m
	}
	return t
}

func main() {
	for _, m := range []int{95, 80, 60} {
		fmt.Print(grade(m), " ")
	}
	fmt.Println(total(10, 20, 30))
}
```

```text
A B C 60
```

## Packages and Modules

| Command or rule | What it does |
|---|---|
| `go mod init example.com/school` | Starts a module (makes `go.mod`) |
| `go run .` | Builds and runs the program |
| `go build` | Makes one program file |
| `go get github.com/google/uuid` | Adds a module |
| `go mod tidy` | Adds what is used, removes what is not |
| `Average` vs `total` | Capital first letter = exported (other packages can use it) |
| `import str "strings"` | Gives a package a short name |
| `import _ "image/png"` | Runs a package's setup only |

## String Methods

The `strings` functions you will use most:

| Function | Example | Result |
|---|---|---|
| `Contains` | `strings.Contains("Green Valley", "Val")` | `true` |
| `HasPrefix` / `HasSuffix` | `strings.HasSuffix("a.txt", ".txt")` | `true` |
| `Index` | `strings.Index("Asha", "h")` | `2` |
| `Split` / `Join` | `strings.Split("a,b", ",")` | `[a b]` |
| `ReplaceAll` | `strings.ReplaceAll("8-A", "-", "")` | `"8A"` |
| `TrimSpace` | `strings.TrimSpace("  hi ")` | `"hi"` |
| `ToUpper` / `ToLower` | `strings.ToUpper("asha")` | `"ASHA"` |
| `Repeat` | `strings.Repeat("-", 5)` | `"-----"` |
| `Fields` | `strings.Fields(" a  b ")` | `[a b]` |

Also remember:

- `len(s)` counts **bytes**. `utf8.RuneCountInString(s)` counts **characters**.
- `s[1:4]` cuts bytes 1 to 3.
- For many joins, use `strings.Builder`: `b.WriteString("x")`, then `b.String()`.

## String Formatting

The `fmt` verbs:

| Verb | Prints | Example output |
|---|---|---|
| `%v` | Any value | `{Asha 78}` |
| `%+v` | Struct with field names | `{Name:Asha Mark:78}` |
| `%d` | Whole number | `78` |
| `%f` / `%.1f` | Decimal / one place | `64.3` |
| `%s` / `%q` | String / quoted string | `"Asha"` |
| `%t` | true or false | `true` |
| `%T` | The type | `int` |
| `%-8s` / `%4d` | Pad right / pad left | `Asha    ` / `  78` |
| `%w` | Wraps an error (`fmt.Errorf` only) | — |

Functions: `Println` (spaces and new line), `Printf` (verbs), `Sprintf` (returns the string), `Errorf` (returns an error).

## Date and Time

| Topic | Syntax | Notes |
|---|---|---|
| Now | `time.Now()` | The current time |
| Make one | `time.Date(2025, time.March, 14, 9, 30, 0, 0, time.UTC)` | Year, month, day, hour, min, sec, nsec, zone |
| Duration | `45 * time.Minute` | Also `time.Second`, `time.Hour` |
| Add / subtract | `t.Add(d)`, `t2.Sub(t1)` | `Sub` gives a duration |
| Compare | `t1.Before(t2)`, `t1.After(t2)` | |
| Format | `t.Format("02/01/2006 15:04")` | Uses the reference date |
| Parse | `time.Parse("2006-01-02", "2025-12-25")` | Same layout rules |

**Reference date** = `Mon Jan 2 15:04:05 2006`. Remember it as **1 2 3 4 5 6**: month 1, day 2, hour 3 (15), minute 4, second 5, year 6.

```go runnable title="Strings and time"
package main

import (
	"fmt"
	"strings"
	"time"
)

func main() {
	parts := strings.Split("Asha,8A,91", ",")
	fmt.Println(strings.ToUpper(parts[0]), len(parts))
	exam := time.Date(2025, time.March, 14, 9, 30, 0, 0, time.UTC)
	fmt.Println(exam.Add(3 * time.Hour).Format("02/01/2006 15:04"))
}
```

```text
ASHA 3
14/03/2025 12:30
```

## Collections

| Collection | Make | Add / change | Notes |
|---|---|---|---|
| Array | `[3]string{"a", "b", "c"}` | `a[0] = "z"` | Fixed size. Copied when assigned |
| Slice | `[]int{1, 2}`, `make([]int, 0, 10)` | `s = append(s, 3)` | Always store the result of `append` |
| Slice part | `s[1:3]` | — | Shares the same array |
| Remove item | `s = append(s[:i], s[i+1:]...)` | — | Keeps the order |
| Copy | `copy(dst, src)` | — | `dst` must have room |
| Map | `map[string]int{"a": 1}`, `make(map[string]int)` | `m["b"] = 2` | Random order |
| Map lookup | `v, ok := m[k]` | `delete(m, k)` | `ok` is false when missing |

`len(x)` works on arrays, slices, maps, strings and channels. `cap(s)` tells how much room a slice has.

## Pointers and Structs

| Topic | Syntax | Notes |
|---|---|---|
| Pointer | `p := &x`, `*p = 5` | `&` = address, `*` = value there |
| Struct | `type Student struct { Name string; Marks int }` | A group of fields |
| Create | `Student{Name: "Asha", Marks: 91}` | Missing fields get zero values |
| Value method | `func (s Student) Grade() string` | Works on a copy |
| Pointer method | `func (s *Student) Add(n int)` | Changes the real struct |
| Embed | `type Teacher struct { Person; Subject string }` | Teacher gets Person's fields and methods |
| Hide | `marks int` + `Marks()` + `SetMarks(m) error` | Small letter = private to the package |
| Custom type | `type Celsius float64` | A new type with its own methods |
| Alias | `type ID = int` | Just another name |

## Interfaces and Generics

| Topic | Syntax | Notes |
|---|---|---|
| Interface | `type Shape interface { Area() float64 }` | A list of methods |
| Satisfy it | Give the type an `Area() float64` method | No `implements` word |
| Any value | `var x any = 5` | `any` = `interface{}` |
| Assert | `n, ok := x.(int)` | `ok` is false if it is not an `int` |
| Type switch | `switch v := x.(type) { case int: ... }` | One case per type |
| Generic function | `func Max[T int \| float64](a, b T) T` | `T` is a type parameter |
| Constraints | `any`, `comparable`, `interface{ ~int \| ~float64 }` | What `T` may be |
| Sort records | `sort.Slice(list, func(i, j int) bool { ... })` | Return true if `i` goes first |

```go runnable title="Structs, interfaces and generics"
package main

import (
	"fmt"
	"sort"
)

type Shape interface{ Area() float64 }

type Rect struct{ W, H float64 }

func (r Rect) Area() float64 { return r.W * r.H }

type Square struct{ S float64 }

func (s Square) Area() float64 { return s.S * s.S }

func Largest[T int | float64](xs []T) T {
	best := xs[0]
	for _, x := range xs {
		if x > best {
			best = x
		}
	}
	return best
}

func main() {
	shapes := []Shape{Rect{2, 3}, Square{4}, Rect{1, 5}}
	sort.Slice(shapes, func(i, j int) bool { return shapes[i].Area() < shapes[j].Area() })
	var areas []float64
	for _, s := range shapes {
		areas = append(areas, s.Area())
	}
	fmt.Println(areas, Largest(areas))
}
```

```text
[5 6 16] 16
```

## Error Handling

| Topic | Syntax | Notes |
|---|---|---|
| Create | `errors.New("msg")` | A simple error |
| Create with details | `fmt.Errorf("roll %d: %w", r, err)` | `%w` wraps the cause |
| Check | `if err != nil { return err }` | Right after every call |
| Sentinel | `var ErrNotFound = errors.New("not found")` | A named error to compare with |
| Is it this one? | `errors.Is(err, ErrNotFound)` | Looks through wrapped errors |
| Is it this type? | `errors.As(err, &myErr)` | Fills `myErr` if found |
| Panic | `panic("bug")` | Stops the program. For bugs only |
| Recover | `defer func() { recover() }()` | Catches a panic inside `defer` |

## File Handling

| Task | Syntax | Notes |
|---|---|---|
| Read all | `data, err := os.ReadFile("a.txt")` | Gives `[]byte` |
| Write all | `os.WriteFile("a.txt", data, 0644)` | Replaces the file |
| Open | `f, err := os.Open("a.txt")` then `defer f.Close()` | Read only |
| Lines | `sc := bufio.NewScanner(f); for sc.Scan() { sc.Text() }` | One line at a time |
| Append | `os.OpenFile(n, os.O_APPEND\|os.O_WRONLY\|os.O_CREATE, 0644)` | Adds to the end |
| Folders | `os.MkdirAll(p, 0755)`, `os.ReadDir(".")` | Make / list |
| Exists? | `_, err := os.Stat(n)` then `errors.Is(err, fs.ErrNotExist)` | |

## JSON

| Task | Syntax | Notes |
|---|---|---|
| Go to JSON | `json.Marshal(v)` | Gives `[]byte` |
| Pretty JSON | `json.MarshalIndent(v, "", "  ")` | Two-space indent |
| JSON to Go | `json.Unmarshal(data, &v)` | Pass a pointer |
| Rename a key | `` `json:"name"` `` | Field tag |
| Skip if empty | `` `json:"name,omitempty"` `` | |
| Never include | `` `json:"-"` `` | |

Only **exported** (capital) fields go into JSON.

```go runnable title="Errors, files and JSON"
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/fs"
	"os"
)

type Config struct {
	School string `json:"school"`
	Year   int    `json:"year"`
}

func main() {
	_, err := os.ReadFile("missing.txt")
	fmt.Println(errors.Is(err, fs.ErrNotExist))

	data, _ := os.ReadFile("data/config.json")
	var c Config
	if err := json.Unmarshal(data, &c); err != nil {
		fmt.Println(err)
		return
	}
	out, _ := json.Marshal(c)
	fmt.Println(string(out))
}
```

```text
true
{"school":"Green Valley School","year":2025}
```

## Goroutines, Channels and Synchronization

The concurrency pieces and their jobs:

| Piece | Syntax | For |
|---|---|---|
| Goroutine | `go work()`, `go func() { ... }()` | Run a job at the same time |
| WaitGroup | `wg.Add(1)`, `defer wg.Done()`, `wg.Wait()` | Wait for jobs to finish |
| Channel | `ch := make(chan int)`, `make(chan int, 5)` | Pass values between goroutines |
| Send / receive | `ch <- v`, `v := <-ch`, `v, ok := <-ch` | |
| End a stream | `close(ch)`, `for v := range ch { }` | Only the sender closes |
| select | `select { case v := <-a: ... case <-time.After(d): ... }` | Wait on many channels |
| Mutex | `mu.Lock()`, `defer mu.Unlock()` | One goroutine at a time |
| Atomic | `var n atomic.Int64; n.Add(1)` | A safe counter |
| Race check | `go run -race main.go` | Finds unsafe sharing |

## Reflection and Context

| Topic | Syntax | Notes |
|---|---|---|
| Type | `reflect.TypeOf(x).Kind()` | `int`, `struct`, `slice` ... |
| Value | `reflect.ValueOf(x)` | |
| Fields | `t.NumField()`, `t.Field(i).Name`, `t.Field(i).Tag.Get("json")` | How JSON reads tags |
| Root context | `context.Background()` | The start of every chain |
| Time limit | `ctx, cancel := context.WithTimeout(parent, 2*time.Second)` | Always `defer cancel()` |
| Listen | `case <-ctx.Done(): return ctx.Err()` | Stop when told |

```go runnable title="Goroutines, channels and context"
package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

func main() {
	results := make(chan int, 3)
	var wg sync.WaitGroup
	for i := 1; i <= 3; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			results <- i * i
		}()
	}
	wg.Wait()
	close(results)
	sum := 0
	for r := range results {
		sum += r
	}
	fmt.Println("sum of squares:", sum)

	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
	defer cancel()
	select {
	case <-time.After(time.Second):
		fmt.Println("finished")
	case <-ctx.Done():
		fmt.Println("stopped:", ctx.Err())
	}
}
```

```text
sum of squares: 14
stopped: context deadline exceeded
```

Forgot a function? Ask Go. On your computer, `go doc strings` lists the strings package, and `go doc strings.TrimSpace` explains one function.

```text
$ go doc strings.TrimSpace
func TrimSpace(s string) string
    TrimSpace returns a slice of the string s, with all leading and trailing
    white space removed, as defined by Unicode.
```

**Easy Way to Remember:**

- Forgot a name? `go doc` it.
- Got an error? Read the first line: file, line number, then the problem.
- Not sure it is right? `go vet` it.

## TL;DR

1. **Types** = `int`, `float64`, `string`, `bool`, `rune`, slices, maps, structs, pointers.
2. **Control flow** = `if`, `switch`, one `for` loop, `range`.
3. **Functions** = many results, variadic, closures, `defer`.
4. **Types with behaviour** = methods, embedding, interfaces, generics.
5. **Safety** = errors as values, `%w`, `errors.Is` / `errors.As`.
6. **Files** = `os.ReadFile`, `os.WriteFile`, `encoding/json`.
7. **Concurrency** = `go`, `WaitGroup`, channels, `select`, `Mutex`, `context`.
8. **Lookup** = `go doc`.

## Common mistakes

The most common mistakes from the whole course, in one place.

<div className="tscroll">

| Mistake | What you see | Why it happens | Fix |
|---|---|---|---|
| An unused variable or import | `declared and not used` | Go refuses unused code | Use it, or remove it |
| `{` on the next line | `syntax error: unexpected newline` | Go adds a `;` at the line end | Keep `{` on the same line |
| `append(s, x)` alone | The new item is lost | `append` returns a new slice | `s = append(s, x)` |
| Writing to `var m map[K]V` | `panic: assignment to entry in nil map` | The map was never made | `m := make(map[K]V)` |
| Changing the `range` value | Nothing changes | `v` is a copy | `list[i].Field = ...` |
| `7 / 2` expected to be `3.5` | `3` | Integer division | `float64(7) / 2` |
| Ignoring `err` | Silent wrong results | Errors are plain values | `if err != nil { ... }` |
| `fmt.Errorf("...%v", err)` to wrap | `errors.Is` returns false | `%v` turns it into text | Use `%w` |
| `main` ends before goroutines | Some output is missing | Go does not wait for them | `sync.WaitGroup` |
| Two goroutines change one value | Wrong totals | A data race | `sync.Mutex` or a channel |
| No `defer cancel()` | Timers stay alive | The context is never released | `defer cancel()` right away |
| A lowercase field in JSON | The field is missing | Only exported fields are seen | Capitalise it, add a tag |

</div>

## Check Yourself

## Exercises

## What to Remember

- Nobody remembers everything. Know a tool exists, and look up the spelling.
- `go doc` explains any package or function, with no internet.
- Every variable and import must be used, and `{` stays on the same line.
- Integer division drops the decimal part. Convert with `float64()` first.
- Store the result of `append`. Make a map before you write to it.
- `range` gives a copy. Change elements with `list[i]`.
- Errors are values. Check every `err`, and wrap with `%w`.
- Capital first letter = exported: other packages and JSON can see it.
- Wait for every goroutine. Lock shared data. `defer cancel()` every context.

You have finished the Go course. Keep practising: build a small project, like a library manager or a marks reporter, using what you learned.
