Go Slices in Depth
A slice is a window over the array that holds its values in memory. That array is called the backing array.
A write through a sub-slice shows in the original
head holds the first two values of nums. Writing head[0] changes the shared array, and the capacity of head reaches the end of that array.
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4}
head := nums[:2]
head[0] = 99
fmt.Println(nums)
fmt.Println("Len:", len(head), "Cap:", cap(head))
}An append past capacity breaks the sharing
head starts with length 2 and capacity 4. Appending three values needs room for five, so the append moves head to a new array and nums keeps its values.
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4}
head := nums[:2]
head = append(head, 5, 6, 7)
fmt.Println(nums)
fmt.Println(head)
}A three-index slice caps the window
The third index sets the capacity of the new slice. With capacity 2, an append cannot write into the array of nums, so it moves to a new array.
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4}
head := nums[:2:2]
fmt.Println("Len:", len(head), "Cap:", cap(head))
head = append(head, 9)
fmt.Println(nums)
fmt.Println(head)
}Length and capacity grow step by step
Each append adds one value to an empty slice. The printed steps show capacity jumping ahead of length when a bigger array is needed.
package main
import "fmt"
func main() {
var nums []int
for _, v := range []int{10, 20, 30, 40} {
nums = append(nums, v)
fmt.Printf("Len: %d Cap: %d\n", len(nums), cap(nums))
}
fmt.Println(nums)
}Example
package main
import "fmt"
func main() {
var queue []int
for _, v := range []int{10, 20, 30} {
queue = append(queue, v)
fmt.Printf("Len: %d Cap: %d\n", len(queue), cap(queue))
}
window := queue[:2:2]
window = append(window, 99)
fmt.Println(queue)
fmt.Println(window)
}Key points
- The length of a slice counts the values inside the window.
- The capacity of a slice counts from the window start to the end of the backing array.
- A sub-slice shares the backing array of the slice it was cut from.
- An append that fits inside the capacity writes into the shared array.
- An append past the capacity moves the slice to a new, bigger array.
- A three-index slice sets its own capacity with a second colon and a max index.
- Capacity grows in steps, and the steps print from len and cap after each append.
In this example, a loop appends 10, 20 and 30 to an empty queue. The printed lines show the capacity steps 1, 2 and 4. A window then takes the first two values with a capacity of 2. Appending 99 to the window moves the window to a new array. The queue still prints its three values, and the window prints 10, 20 and 99.
Note
Cutting a window never copies values, so a small window can keep a big array in memory.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท Go Playground
Exercise
A slice is cut as nums[:2:2]. What is the capacity of the new slice?