Beginner · Collections and structs · topic 12 of 46 · 40 min
Slices in depth: length, capacity and sharing
Explanation
What a slice really is
A slice value is just three numbers: a pointer to the first element it can see in an underlying array, a length (how many elements it shows) and a capacity (how many elements there are from its start to the end of that array). The data itself lives in the array.
arr := [6]int{0, 1, 2, 3, 4, 5}
s := arr[1:4]
fmt.Println(s, len(s), cap(s)) // [1 2 3] 3 5Sharing: one change, two slices
a := []int{1, 2, 3, 4}
b := a[:2] // [1 2], shares a's array, cap 4
b[0] = 100
fmt.Println(a) // [100 2 3 4]
b = append(b, 99) // room left in the array: writes into a[2]!
fmt.Println(a) // [100 2 99 4]
fmt.Println(b) // [100 2 99]When append makes a new array
If the slice has no spare capacity, append allocates a bigger array, copies the elements over, and returns a slice pointing to the new array. From then on, the old and new slices no longer share data. Because you cannot always predict which case happens, always use the result of append and do not rely on sharing after append.
x := make([]int, 3, 3) // len 3, cap 3, full
y := append(x, 4) // no room: new array
y[0] = 7
fmt.Println(x, y) // [0 0 0] [7 0 0 4]Protecting data: copy and the full slice expression
src := []int{1, 2, 3}
dst := make([]int, len(src))
n := copy(dst, src) // copies min(len(dst), len(src)) elements
dst[0] = 50
fmt.Println(src, dst, n) // [1 2 3] [50 2 3] 3
safe := src[0:2:2] // low:high:max, cap is max-low = 2
safe = append(safe, 9) // must allocate, src untouched
fmt.Println(src, safe) // [1 2 3] [1 2 9]nil slice versus empty slice
var a []int // nil slice
b := []int{} // empty, not nil
fmt.Println(a == nil, b == nil) // true false
fmt.Println(len(a), len(b)) // 0 0
a = append(a, 1) // append works on nil slices tooOne place they differ: encoding/json turns a nil slice into null and an empty slice into []. An API that must return [] for "no orders" should start with an empty slice.
The slices package (Go 1.21+)
import "slices"
prices := []int{450, 120, 999, 60}
slices.Sort(prices) // [60 120 450 999]
fmt.Println(slices.Contains(prices, 120)) // true
fmt.Println(slices.Index(prices, 999)) // 3
fmt.Println(slices.Index(prices, 5)) // -1 (not found)
fmt.Println(slices.Max(prices)) // 999
slices.Reverse(prices) // [999 450 120 60]
fmt.Println(slices.Equal(prices, []int{999, 450, 120, 60})) // trueGo 1.21 also added built-in min and max functions for values: min(3, 7, 1) is 1. Before that, people wrote small helpers or used the older sort package (sort.Ints, sort.Strings), which you will still see in older code.
Removing an element
s := []string{"a", "b", "c", "d"}
i := 1
s = append(s[:i], s[i+1:]...) // classic way: [a c d]
// or, Go 1.21+:
// s = slices.Delete(s, i, i+1)Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Slices in depth: length, capacity and sharing · 1/7
Slices are simple to use, but to avoid bugs you need to know what is inside one.
Prefer a video by a teacher? Find free videos on “golang slice length capacity append internals explained” on YouTube ↗
In real life: Zerodha-style top gainers
A stock app has today's price changes for many companies. To show the top 3 gainers, it clones the slice (so the original order used elsewhere is untouched), sorts the clone, and takes the last three. Without the clone, sorting would silently reorder the shared data that another part of the screen is still using.
package main
import (
"fmt"
"slices"
)
func main() {
changes := []float64{1.2, -0.8, 3.5, 0.4, 2.1, -1.9}
sorted := slices.Clone(changes)
slices.Sort(sorted)
top3 := sorted[len(sorted)-3:]
slices.Reverse(top3)
fmt.Println("Top gainers %:", top3) // [3.5 2.1 1.2]
fmt.Println("Original:", changes) // unchanged order
}Practice (0/4 done)
What does this print? s := make([]int, 2, 10) fmt.Println(len(s), cap(s))
What does this print? a := []int{1, 2, 3, 4} b := a[1:3] b[0] = 9 fmt.Println(a)
What does this print? a := []int{1, 2, 3} b := a[:1] b = append(b, 7) fmt.Println(a)
Write func removeAt(s []string, i int) []string that removes the element at index i WITHOUT changing the caller's original slice. Show in main that the original still has all its elements.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Strings, bytes and runesCollections and structs · same module
- Maps: key-value look-upsCollections and structs · same module
- Go language interview questionsCareers and interviews
- Pointers without fearPointers, interfaces and errors
- Slices and stringsRust Programming · Borrowing, slices and lifetimes
- Arrays: lists of valuesJavaScript Programming (language in depth) · Arrays and objects
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Which three things make up a slice header?
Answer: Pointer to array, length, capacity
A slice is a pointer, a length and a capacity.
var s []int; s = append(s, 5). What happens?
Answer: It works; s is [5]
append works on a nil slice and allocates an array.
How many elements does copy(dst, src) copy?
Answer: The smaller of len(dst) and len(src)
copy never grows dst; it copies min(len(dst), len(src)) elements.
What does slices.Index(s, x) return when x is not in s?
Answer: -1
Like many Go search functions, it returns -1 for "not found".
Why use s[0:2:2] before appending?
Answer: To limit capacity so append must allocate and cannot overwrite the original array
The third index sets capacity; with no spare room, append copies to a new array.
Can you compare two slices with ==?Related
Answer: No, only a slice with nil
Slices are not comparable; use slices.Equal to compare contents.
From Arrays and slices
How does Go provide polymorphism without inheritance?Related
Answer: Interfaces satisfied implicitly
Any type with the right methods can be used through an interface.
Can you do p++ on a pointer in normal Go code?Related
Answer: No, Go has no pointer arithmetic
Pointer arithmetic is not allowed (outside the unsafe package).
What does a slice contain?Related
Answer: A pointer and a length
A slice is a fat pointer: where it starts and how many items.
From Slices and strings
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 3 more topics in “Collections and structs” before its exam: Strings, bytes and runes, Maps: key-value look-ups, Structs and methods.
- Module 3 exam: Collections and structs · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Pointers, interfaces and errors. 46 topics left in the course before the final exam.