A slice is a data type that is mutable and points to an underlying array
consisting of an ordered sequence of elements that belong together.
Since the length of a slice changes dynamically, in the Golang world it
is widely used when the total number of elements is unknown or not fixed.
Contents
Abstract
Introduction
Definition and declaration
Indexing
Modifying elements
Appending elements
Removing elements
Multidimensional slices
Summary
Introduction
Three important points that help use slices in any Golang project
proficiently:
A slice holds a pointer to an underlying array. The array contains
the values of the elements, not the slice.
1
2
a:= []int{10, 20, 30}
fmt.Printf("%p\n", a) // 0xc000018100
Length of a slice means how many elements from the underlying array
it uses.
1
2
a:=make([]int, 3, 5)
fmt.Println(len(a)) // 3
Capacity of a slice means the maximum number of elements the
underlying array can contain.
1
fmt.Println(cap(a)) // 5
When more elements than the existing capacity of the underlying array
need to be appended, the slice automatically resizes the array by
copying elements to a new and extended array. This gives the program
the ability to allocate memory dynamically when the number of elements
is not fixed.
Unlike arrays, for slices index bounds are not checked at compile-time.
Therefore, only a run-time error happens when an out-of-index is called
during the execution of a program.
1
2
a:= []int{10, 20, 30}
fmt.Println(a[3]) // panic: runtime error: index out of range [3] with length 3
Definition and declaration
General schema I for declaring a slice
[]data_type{elements}
All elements must be the same data type, e.g. int or byte or
string, etc.
If the values of the elements along with the length and capacity are
not declared, the default slice is empty, the length is zero and the
capacity is also zero which will be changed dynamically upon appending
new elements to the slice.
1
a:= []int{} // a = [], length = 0, capacity = 0
General schema II for declaring a slice
var := make([]data_type, length, capacity (optional))
1
2
3
4
5
6
a:=make([]int, 3)
// a = [], length = 3, capacity = 0 --> zeroed slice with a length of 3
b:=make([]int, 3, 5)
// b = [], length = 3, capacity = 5 --> pre-allocated capacity of
// 5 elements in the underlying array
Sometimes it is hard to understand the values of each element with
Println() function when the data type is string:
1
2
3
msg:= []string{"hello world", "mars", "hello saturn", "jupiter"}
fmt.Println(msg)
// output: [hello world mars hello saturn jupiter]
With Printf() function and %q verb this problem can be solved:
Each element saved in the underlying array of a slice corresponds to an
index number starting from 0 and counting up. In Golang, it is not
allowed to index backward with a negative number.
1
2
3
4
fmt.Println(msg[0]) // hello world
fmt.Println(msg[1]) // mars
fmt.Println(msg[2]) // hello saturn
fmt.Println(msg[3]) // jupiter
It is possible to concatenate the same data types using indices.
1
2
fmt.Println(msg[0] +" and hello from "+msg[1])
// output: hello world and hello from mars
Modifying elements
With the index number of the underlying array, elements of a slice
can be updated.
Note: Defining the capacity is optional. But the performance of a
program is enhanced significantly when the underlying array’s initial
capacity is defined.
Removing elements
Golang does not have any built-in function to delete elements from a
slice. To remove an element, the slice needs to be sliced out and
appended together again.
If an element needs to be removed whose index is i, first all the
items before and after the i-th index must be sliced out and then
those sliced out items have to be appended together.
So, the new slice will be,
slice = append(slice[:i], slice[i+1:]...)
Since the removal process does not require the slice to increase the
capacity of the underlying array, the array capacity remains the same.
A slice does not decrease the capacity of the underlying array
dynamically.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a = append(a[:0], a[1:]...) // removing the element at index 0
fmt.Println(a) // [20 30 40 50 60]
fmt.Print("Length: ")
fmt.Println(len(a)) // Length: 5
fmt.Print("Capacity: ")
fmt.Println(cap(a)) // Capacity: 10
fmt.Println("")
a = append(a[:3], a[4:]...) // removing the element at index 3
fmt.Println(a) // [20 30 40 60]
fmt.Print("Length: ")
fmt.Println(len(a)) // Length: 4
fmt.Print("Capacity: ")
fmt.Println(cap(a)) // Capacity: 10
Multidimensional slices
A slice can consist of other slices as elements to provide multidimensional
feature.
This tutorial demonstrates the concept of slices in Golang, how they
ease the development phase, and dynamically increases the capacity
of the underlying array during run-time.