Slice vs Array´
In Go, there are two common data structures for “sequence data”: Array and Slice. They are similar in syntax, but the differences in behavior and usage scenarios are crucial. In short, Array is fixed-length (Static Array), while Slice is dynamic-length (Dynamic Array).
Array
// Declare an Array of 5 intarray := [5]int{1, 2, 3, 4, 5}
// Access or modify an elementarray[0] = 100
// Let compiler automatically infer array lengthb := [...]string{"Penn", "Teller"}Slice
Slice is built on top of Array. It does not store data directly but points to the underlying Array while recording the current length and capacity.
func main() { // Create Slice (length 3, capacity 5) s := make([]int, 3, 5) fmt.Println(s, len(s), cap(s)) // [0 0 0] 3 5
// Modify and read s[0] = 10 s[1] = 20 s[2] = 30
// Expand s = append(s, 40, 50) fmt.Println(s, len(s), cap(s)) // [10 20 30 40 50] 5 5
// Expand s = append(s, 60) fmt.Println(s, len(s), cap(s)) // [10 20 30 40 50 60] 6 10
// Sub Slice sub := s[1:4] fmt.Println(sub) // [20 30 40]}Understanding Slice’s Automatic Expansion Mechanism
When performing append on a Slice:
- If the underlying Array still has space → just extend the length (O(1))
- If the capacity is insufficient → Go will create a larger Array and copy the old data over (O(n))
func main() { // Amortized analysis s := []int{1} fmt.Println(len(s), cap(s)) // 1 1, O(1)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 2 2, O(1)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 3 4, O(n)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 4 4, O(1)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 5 8, O(n)}You can see that the capacity mostly doubles, this automatic expansion strategy balances performance and memory utilization.
Range
Iterating through the array contents.
func main() { s := []string{"apple", "banana", "cherry"}
for i, v := range s { fmt.Println(i, v) }}
// 0 apple// 1 banana// 2 cherry: Slice
: is used to take a portion of the Slice, formatted as slice[low:high] which means starting from index low (inclusive) to index high (exclusive):
func main() { s := []int{10, 20, 30, 40, 50}
fmt.Println(s[1:3]) // [20 30] fmt.Println(s[:2]) // [10 20], omitting the lower bound means 0 fmt.Println(s[2:]) // [30 40 50], omitting the upper bound means len(s)}Although s[a:b] creates a new Slice, it still shares the same underlying array. Modifying sub Slice’s content will affect the original data:
sub := s[1:3]sub[0] = 99fmt.Println(s) // [10 99 30 40 50]fmt.Println(sub) // [99 30]Copy
Since Slices share the underlying Array, if you want to create a completely independent copy, you should use copy():
s := []int{1, 2, 3}t := make([]int, len(s))copy(t, s)
t[0] = 99fmt.Println(s) // [1 2 3]fmt.Println(t) // [99 2 3]Summary
| Comparison Item | Array | Slice |
|---|---|---|
| Length | Fixed | Variable |
| Type Definition | [N]T | []T |
| Contains Capacity Information | No | Yes |
| Expandable | No | Yes (append) |
| Passing Overhead | Copy entire array | Pass reference (lightweight) |
| Suitable Scenarios | Known fixed length | Unknown length, needs flexible data growth |
You can start solving problems related to Go Array on Codewars.
Further Reading
- Go Slices: usage and internals - go.dev
- [Golang] Array and Slice - PJCHENder
- If you are a beginner in Go, avoid this Slice pitfall! - Flo Woelki
- Go Class: 10 Slices in Detail - Matt KØDVB