Taking part of a slice
Sometimes you need only part of a slice, such as the first few items. Slicing gives you that part. Write the start index and the end index in brackets with a colon between them. The item at the end index is not included.
letters := []string{"a", "b", "c", "d", "e"}
middle := letters[1:4]
fmt.Println(middle) // [b c d]
fmt.Println(letters) // [a b c d e]
letters[1:4] holds the items at indexes 1, 2 and 3. The original slice is unchanged and still has five items. The length of s[i:j] is always j - i.
Leaving out an index
Leave out the start to begin at index 0. Leave out the end to run to the very end:
fmt.Println(letters[:2]) // [a b]
fmt.Println(letters[3:]) // [d e]
letters[:] is the whole slice, and an empty range such as letters[2:2] is an empty slice with length 0.
A slice shares its items
A Go slice copies nothing. It is a window onto the same items. Changing the window changes the original:
middle[0] = "X"
fmt.Println(letters) // [a X c d e]
The same sharing causes a subtler trap. middle has length 3 but capacity 4, because it can grow into the items after it in letters. Calling append on a sub-slice can therefore overwrite items of the original slice. For example, after m := letters[1:4], running m = append(m, "Z") changes letters to [a b c d Z]: the new item was written into the place that used to hold "e". If you need an independent copy, make one explicitly, for example with slices.Clone.
Length and capacity of a slice
For middle := letters[1:4], len(middle) is 3 and cap(middle) is 4. The capacity counts from the slice's first item to the end of the memory it points into, which is why it can grow without moving. Most of the time you only need len; capacity matters for the append behaviour described above.
Dropping items from the ends
Slicing with len picks items counted from the end, since Go has no negative indexes. On the five letters, letters[:len(letters)-1] is [a b c d], without the last one, and letters[len(letters)-2:] is [d e], the last two. Strings can be sliced the same way: "hello"[1:3] is el.
To get a copy that doesn't share items, clone the part: after c := slices.Clone(letters[1:4]), setting c[0] = "Q" leaves letters as it was.
Bounds
Go checks slice indexes when the program runs, so a bad index is not a compile error; it is a panic that stops the program.
Both indexes must fit the slice. letters[:6] on a five-item slice panics with slice bounds out of range [:6] with capacity 5. Also, the start can't be larger than the end.
The task: a race
runners holds Ana, Ben, Cleo, Dan, Eve in finishing order. The task builds two new slices.
podiumis the first three runners:runners[0:3]. Index 3 is not included, so this takes the items at 0, 1 and 2.restis everyone else:runners[3:]. With no end index, the slice runs to the end.
runners := []string{"Ana", "Ben", "Cleo", "Dan", "Eve"}
podium := runners[0:3]
rest := runners[3:]
fmt.Println(podium[0], podium[1], podium[2])
fmt.Println(rest[0], rest[1])
fmt.Println(len(runners))
The output is:
Ana Ben Cleo
Dan Eve
5
The last line is 5 because slicing doesn't remove anything from runners.
Key takeaways
s[i:j]holds the items from indexiup to, not including,j.- Leaving out
imeans 0; leaving outjmeans the end. - The result shares its items with
s, so changes show up in both.
