Slices

Slices over Arrays

In Go, we seldom use raw arrays directly. Rather, Slices are more common.

Define Slices

1
2
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s := arr[2:6]

In the code above, arr is an array and s is a slice. In Go, slices are half open. So s is [2,3,4,5]

The output will be

1
arr[2:6] = [2 3 4 5]

Note if array is defined as below, it’s not an array, but a slice, ... is required between []

1
arr := []int {0,1,2,3}

The slices can be flexible with boundaries:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s := arr[2:6]

fmt.Println("arr[2:6] =", s)
fmt.Println("arr[2:] =", arr[2:])
fmt.Println("arr[:6] =", arr[:6])
fmt.Println("arr[:] =", arr[:])
}

The outputs will be:

1
2
3
4
arr[2:6] = [2 3 4 5]
arr[2:] = [2 3 4 5 6 7]
arr[:6] = [0 1 2 3 4 5]
arr[:] = [0 1 2 3 4 5 6 7]

Slice is a View

A slice is not a value type, and it doesn’t hold any data. According to Go Doc, it’s a view of array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

func updateSlice(s []int) {
s[0] = 100
}

func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

s1 := arr[2:]
fmt.Println("Before updateSlice(s1)")
fmt.Println("s1 =", s1)
s2 := arr[:]
fmt.Println("s2 =", s2)

fmt.Println("After updateSlice(s1)")
updateSlice(s1)
fmt.Println("s1 =", s1)
fmt.Println("arr =", arr)

fmt.Println("After updateSlice(s2)")
updateSlice(s2)
fmt.Println("s2 =", s2)
fmt.Println("arr =", arr)
}

The outputs will be:

1
2
3
4
5
6
7
8
9
Before updateSlice(s1)
s1 = [2 3 4 5 6 7]
s2 = [0 1 2 3 4 5 6 7]
After updateSlice(s1)
s1 = [100 3 4 5 6 7]
arr = [0 1 100 3 4 5 6 7]
After updateSlice(s2)
s2 = [100 1 100 3 4 5 6 7]
arr = [100 1 100 3 4 5 6 7]

Since slice is a view of the array, when we change the value in slice, the corresponding value in array will be changed as well.

So we could refactor printArray in Arrays post from Talkgolang.com

1
2
3
4
5
6
7
8
9
10
11
func printSlice(arr []int) {
arr[0] = 100
for i, v := range arr {
fmt.Println("Index:", i, "---", "Value:", v)
}
}
func main(){
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
printSlice(arr[:])
fmt.Println(arr)
}

The outputs will be:

1
2
3
4
5
6
7
8
9
Index: 0 --- Value: 100
Index: 1 --- Value: 1
Index: 2 --- Value: 2
Index: 3 --- Value: 3
Index: 4 --- Value: 4
Index: 5 --- Value: 5
Index: 6 --- Value: 6
Index: 7 --- Value: 7
[100 1 2 3 4 5 6 7]

Note, when invoking printSlice, we can only pass the argument as a slice arr[:] not as an array arr. printSlice(arr) will report error.

Re-slicing

Slices can be based on another slice.

1
2
3
4
arr := [...]int{0,1,2,3,4,5,6,7,8,9}
s := arr[:8]
s1 := s[:5]
s2 := s1[2:]

The outputs will be

1
2
s1: [0 1 2 3 4]
s2: [2 3 4]

Advanced Slicing

Let’s review some more complex concepts in slices.

Slicing “out of index”

See the example below:

1
2
3
arr := [...]int{0,1,2,3,4,5,6,7}
s1 := arr[2:6]
s2 := s1[3:5]

In the example above, s1 only has 4 elements, but s2 tries to slice “out of the index”. What’s s1 and s2?

Here’s the output:

1
2
s1: [2 3 4 5]
s2: [5 6]

It’s interesting that:

  • slicing out of the s1 index won’t report error
  • the last element 6 in s2 does not even exist in s1

If we attempt to get 6 in s1 and assume its index is 4, by running fmt.Println(s1[4]), we will get a runtime error

1
panic: runtime error: index out of range [4] with length 4

When a slice is created, it contains the following information:

  • pointer, the starting index of the slice
  • length, the length of the slice
  • capacity, the capacity of the original array
  • So the slice can be extended as long as its index does not exceed the capacity.

  • Another important feature of slice is, it can be extended forward but not backward. e.g. elements 0 and 1 in the original array is not visible to s2 slice, as s1 pointer only starts at element 2.

We could get the length and capacity with built-in len and cap methods.

1
2
fmt.Printf("s1 = %v, len(s1) = %d, cap(s1) = %d\n", s1, len(s1), cap(s1))
fmt.Printf("s2 = %v, len(s2) = %d, cap(s2) = %d\n", s2, len(s2), cap(s2))

The output will be:

1
2
3
arr = [0 1 2 3 4 5 6 7]
s1 = [2 3 4 5], len(s1) = 4, cap(s1) = 6
s2 = [5 6], len(s2) = 2, cap(s2) = 3

So the capacity of s2 is 3, if it wants to access element beyond 7 in the original arr, it will report runtime error:

1
panic: runtime error: slice bounds out of range [:7] with capacity 6