21. Slices

A window into an array!

Slice Type

Array size is fixed in Go. We can not add more elements to an existing array. This is limiting from a programmer's point of view. Go provides slices to address this concern.

Slices in Go can grow. Arrays are fixed length sequence where as slices are variable length sequence in Go. Slices are very lightweight data structures.A slice has three components: a pointer, a length, and a capacity.

Declaring slice

While declaring slice, we need to specify type of element slice will hold with empty pair of [].

[]T defines a slice of type T.

main.go
func printSlice(s []string) {
	fmt.Printf("size: %v, capacity:%v,value:%v\n",
		len(s), cap(s), s)
}
func main() {
	// declaring slice
	var names []string
	printSlice(names)
	//output size: 0, capacity:0,value:[]
}

While declaring an array, we need to specify size while slice declaration has empty size. a is an array below while s is a slice.

main.go
var a [5]int
var s []int

Creating slice

Declaring slice using var does not allocate backing array for slice. Instead, we have to create slice using make function. Make function accepts 3 parameters, first is type of slice, second is length of slice to create and optionally third parameter as capacity of slice.

s := make([]T,length,capacity)

main.go
names = make([]string, 5)
printSlice(names)
// output : size: 5, capacity:5, value:[    ]

If we know in advance, what capacity we need for slice, we can make slice with that capacity.

main.go
names = make([]string, 5, 20)
printSlice(names)
// output : size: 5, capacity:20, value:[    ]

Accessing slice elements

Since slice is window into an array, its elements can be accessed by using square brackets [] with an index that begins at 0 similar to array access.

main.go
greetings := make([]string, 2)
greetings[0] = "Hello"
greetings[1] = "World"
printSlice(greetings)
// output: size: 2, capacity:2, value:[Hello World]

Zero values

When new slice is created using make all its elements get initialized to its zero values.

main.go
numbers := make([]float64, 8)
months := make([]string, 12)
fmt.Println(numbers)
fmt.Println(months)
// output : 
// numbers => [0 0 0 0 0 0 0 0]
// months => [           ]

Slice literals

If we know in advance what values a slice will start with, we can initialize the slice with those values using slice literal. We do not need to make function call when using slice literals.

[]T{values}

main.go
data := []string{
		"Hello",
		"World!",
	}
printSlice(data)
//out size: 2, capacity:2, value:[Hello World!]

Slice from an existing array

We can create slice from an existing array. To create slice from existing array, we need to specify start index and end index using syntax array[startIndex:endIndex]

If starting index is zero, it can be skipped. If end index equals length of array, it can be skipped.

main.go
days := [7]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
	weekdays := days[0:5]
	// if first element of slice is same as first 
	//element of array, start index can be skipped
	weekdays = days[:5]
	printSlice(weekdays)

	weekend := days[5:7]
	// if last element of slice is same as last 
	// element of array, end index can be skipped
	weekend = days[5:]
	printSlice(weekend)

	alldays := days[0:7]
	// if slice has all array elements, both start 
	// index and end index can be skipped
	alldays = days[:]
	printSlice(alldays)

Nil and empty slice

Go has both nil and empty slices. When we use var declaration, it initializes nil slice provided we do not use slice literal. If we use short variable declaration and create slice using make, it initializes empty slice. In case of nil slice, no backing array is created, while for empty slice backing array is present.

use len(s) == 0, and not s == nilfor checking for empty slice.

main.go
// empty and nil slices
s := make([]string, 3)[3:]
var e []string
printSlice(s)
printSlice(e)

fmt.Println("Is s nil => ", s == nil)
fmt.Println("Is e nil => ", e == nil)

Iterating over slice

Iteration over slice is similar to array iteration. Preferred way is to for...range

main.go
// iterating over slices
for _, day := range alldays {
	fmt.Println(day)
}

Type of slice and comparison

Unlike array, slice types are defined what type of data slice store. Comparison is not possible using == unlike arrays. For most use cases where comparison needs to be done, we need to write our own comparison code

main.go
func testEquality(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

Append

The advantage slices have over arrays is the fact that slices can increase capacity and hold more elements at run-time. To add elements to slice, we use built in append function.

append can be used to add one or more elements to slice. Append operation may create new slice if new elements to add do not fit in existing capacity of slice.

Whenever we do append operation, we reassigned the returned slice to original slice. Returned slice may or may not be new slice.

s := append(s, e)

main.go
s := make([]string, 0)
// appending elements
s = append(s, "a")
printSlice(s)
s = append(s, "e")
printSlice(s)
s = append(s, "i")
printSlice(s)
s = append(s, "o")
s = append(s, "u")
printSlice(s)
// output
// size: 1, capacity:1, value:[a]
// size: 2, capacity:2, value:[a e]
// size: 3, capacity:4, value:[a e i]
// size: 5, capacity:8, value:[a e i o u]

GitHub Code

Blog Posts

Videos

Last updated