# Slices and maps

> Source: https://learn-go.org/slices-and-maps/
> Part of Learn Go, free to read.

## Slices

A slice is a view onto an array: a pointer, a length and a capacity.

```go
nums := []int{1, 2, 3}
nums = append(nums, 4)            // append returns a new slice header — reassign it
fmt.Println(len(nums), cap(nums)) // 4 4

empty := make([]int, 0, 100)      // length 0, capacity 100 — no reallocation for 100 appends
```

Preallocating capacity when you know the size is the cheapest performance win in Go.

```go
out := make([]string, 0, len(users))
for _, u := range users {
    out = append(out, u.Name)
}
```

### Slicing

```go
s := []int{0, 1, 2, 3, 4}
s[1:3]    // [1 2]
s[:2]     // [0 1]
s[2:]     // [2 3 4]
```

:::danger The aliasing trap
Slicing does not copy. Two slices can share the same backing array, and `append` may write through to the other one.

```go
a := []int{1, 2, 3, 4, 5}
b := a[:2]
b = append(b, 99)      // cap(b) is 5, so this writes into a's array
fmt.Println(a)         // [1 2 99 4 5]
```

To get an independent copy: `b := slices.Clone(a[:2])`, or use a full slice expression `a[:2:2]` to cap the capacity so `append` must reallocate.
:::

### The `slices` package

```go
import "slices"

slices.Contains(nums, 3)
slices.Index(nums, 3)
slices.Sort(nums)
slices.SortFunc(users, func(a, b User) int { return cmp.Compare(a.Age, b.Age) })
slices.Reverse(nums)
slices.Clone(nums)
slices.Max(nums)
```

These are standard library. Generated Go still frequently hand-rolls `contains` and `min`, which is a good signal that the code came from older training data.

## Maps

```go
ages := map[string]int{"ada": 36, "alan": 41}
ages["grace"] = 45
delete(ages, "alan")

age, ok := ages["ada"]      // the comma-ok idiom
if !ok {
    // not present — distinguishes "missing" from "present but zero"
}
```

The comma-ok form matters: `ages["nobody"]` returns `0` with no error, which is indistinguishable from a real zero without it.

```go
counts := make(map[string]int)
counts["x"]++                // works — missing keys read as the zero value
```

### Iteration order is random

Deliberately. Go randomises it so you cannot depend on it.

```go
keys := slices.Sorted(maps.Keys(m))    // Go 1.23+
for _, k := range keys {
    fmt.Println(k, m[k])
}
```

Any code that produces output by ranging a map directly will produce different results between runs. This is a common source of flaky tests in generated Go.

### Maps are not safe for concurrent use

Concurrent read and write panics at runtime with "concurrent map writes". Use a `sync.Mutex`, or `sync.Map` for the specific case of many readers and few writers.

## Exercise

```go
package main

import "fmt"

func main() {
	words := []string{"go", "is", "fun", "go", "is", "fast", "go"}
	// Count how many times each word appears, then print the counts
	// for the words "go", "is", "fun" and "fast" — in that order.
	fmt.Println()
}
```

## Common questions

### Slice or array?

Slices, almost always. Arrays have a fixed size that is part of their type (`[5]int` and `[6]int` are different types) and they are copied by value on assignment. Arrays are for the rare case where the size is genuinely fixed and known at compile time.

### Why does `append` sometimes modify the original slice?

Because slices share a backing array. If there is spare capacity, `append` writes into it rather than allocating — and anything else viewing that array sees the change. `slices.Clone` when you want independence.

### How do I get sorted map iteration?

Collect the keys, sort them, then range the sorted keys. `slices.Sorted(maps.Keys(m))` does it in one line on Go 1.23 and later.
