Week 1 · lesson

Arrays and slices

Understand slice headers, shared backing arrays, capacity and append.

slicesarraysmemory

Today’s outcome

  • Explain the central idea in your own words.
  • Predict the behavior of a small example before running it.
  • Implement the warm-up and core tasks, then verify them.

Build the mental model first

An array owns a fixed number of values. Its length is part of its type, so [3]int and [4]int are different types. Assignment copies the whole array. This is predictable, but most Go APIs need a view whose length can change. That view is a slice.

A slice is a small descriptor containing a pointer to an underlying array, a length, and a capacity. Copying a slice copies that descriptor, not its elements. Two slices can therefore observe and change the same storage. This one fact explains most slice surprises.

Gotcha — append may or may not detach.

If capacity remains, append writes into the shared array. If it is exhausted, append allocates another array. Never make correctness depend on guessing the growth strategy.

Length, capacity and full slice expressions

Length is the range you can index now. Capacity is how far the slice can grow before it needs new storage. The expression a[low:high:max] limits capacity to max-low; this can deliberately force a later append to detach.

Nil, empty and copied slices

A nil slice and an empty non-nil slice both have length zero and work with append and range. They can encode differently at an API boundary, so choose intentionally. To detach data, allocate and copy, use slices.Clone, or append into a fresh zero-length slice.

Try · predict before you run

example 1.goOpen Playground ↗
a := [3]int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a, b) // [1 2 3] [99 2 3]
example 2.goOpen Playground ↗
a := []int{1, 2, 3, 4}
b := a[1:3]
b[0] = 20
fmt.Println(a) // [1 20 3 4]
example 3.goOpen Playground ↗
s := make([]int, 0, 2)
s = append(s, 10, 20)
t := append(s, 30)
fmt.Println(len(t), cap(t)) // 3 4

Check the model

Quick review

Score 70% or higher. Your best score is saved.

1. After s := make([]int, 2, 5), what are len(s) and cap(s)?
2. What does this print?
a := []int{1, 2, 3, 4}
b := a[:2]
b = append(b, 99)
fmt.Println(a)
3. What is the zero value of a slice?
4. Why does addOne(s []int) fail to grow the caller’s slice?
func addOne(s []int) { s = append(s, 1) }
5. Which operation makes an independent shallow copy of s?
6. What does a full slice expression a[low:high:max] control?
7. When are two arrays directly comparable?
8. What does copy(dst, src) return?
9. Why can reusing one inner slice break a 2D slice?
10. Which is true about nil and empty slices?

Make it yours

Coding tasks

Reverse in place

warmup

Solve a small, direct version. Keep the function pure and test an empty input.

Hint

Swap symmetric indexes while moving toward the middle.

Dedup consecutive values

core

Implement the central behavior and cover normal, empty and boundary cases.

Hint

Keep a write index behind the read index and return s[:w].

Rotate by k

stretch

Optional: improve the design or complexity and explain the tradeoff.

Hint

Normalize k, then use three in-place reversals.

Reflect

What clicked? What still feels fuzzy?