liveBook · Manning

Slicing

As explained in 100 Go Mistakes and How to Avoid Them

Slicing is the Go operation that selects a half-open range of elements from an array or slice.

A common mistake is treating a sliced range as an independent array. In Go, `s[low:high]` selects elements from `low` up to, but not including, `high`. The result has its own length and capacity but shares the original backing array. Changes to elements in the shared range are visible through both slices. section 3.4

An append changes an element you did not pass

Consider `s1 := []int{1, 2, 3}; s2 := s1[1:2]; s3 := append(s2, 10)`. The result is `s1=[1 2 10]`, `s2=[2]`, and `s3=[2 10]`. Although `s2` exposes only one element, it has capacity for another. `append` uses that space in the shared array, overwriting `s1[2]`. section 3.9

When passing a prefix to a function, choose the protection you need. `f(s[:2:2])` limits the new slice’s capacity to its length, so an append inside `f` cannot overwrite `s[2]` in the existing array. It does not isolate the first two elements: direct updates to them remain visible through `s`. Make a copy when the function must not change the original elements either. section 3.9

Figure 3.12 s[0:2] creates a two-length, three-capacity slice, whereas s[0:2:2] creates a two-length, two-capacity slice.

Compare the capacities of `s[0:2]` and `s[0:2:2]`: the third index controls whether a subsequent append has room in the original backing array. section 3.9

A short result can also retain much more memory than its length suggests. Returning `foos[:2]` from a slice of 1,000 structs leaves the backing array reachable. If the structs contain slices or other pointer fields, data held by the 998 inaccessible entries is not reclaimed in the demonstrated GC scenario. Copy the two wanted elements into a new slice, or clear the unwanted pointer fields before returning the prefix. section 3.10.2

The shared-array question matters in concurrent code too. Appending from separate goroutines to a shared slice with spare capacity can write the same backing-array location and cause a data race. Give each goroutine a copy when it needs an independently extendable slice. section 9.9

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Traces length, capacity, shared storage, and append behavior through examples, then examines GC retention of pointer-bearing elements.
Examples
Shows an appended subslice overwriting an original element and a two-element prefix retaining data from 1,000 structs.
What it assumes
Introduces backing arrays, length, and capacity before relying on them to explain slicing behavior.
What you can do after
Diagnose/debug with it append side effects and retained memory, then choose capacity limits, copies, or clearing as appropriate.
How it connects
Connects slicing to backing arrays, slice capacity, append, copying, garbage collection, and data races.

Also explained in

Compare how 16 books explain slicing →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.