Gopher

Slices in Golang

Published on 10 April 2020
Last Updated on 10 April 2020

An array is a fixed length, however a slice is dynamically sized.

In Golang, a slice is a dynamically sized view into an array. A Slice can also be defined as a segment of an array.

Slices are references to arrays. A slice itself does not store any data instead it refers to the data stored in the underlying array.

Components of slice in Golang

Slices in Golang may have following components:

  • Pointer to underlying array.
  • Length of the slice.
  • Capacity of the slice.
  • Slice bounds.

Declaration of slices

Program given below demonstrates various ways by which slices can be declared in Golang.

// program to demonstrate declaration of slices in Golang
package main

import "fmt"

func main() {
	// declaration of array
	var array1 = [8]int{1, 2, 3, 4, 5, 6, 7, 8}
	fmt.Println("array1 is", array1)

	// initializing a slice from an existing array
	var slice1 = array1[0:4]
	fmt.Println("slice1 is", slice1)

	// resizing an existing slice
	slice1 = slice1[0:6]
	fmt.Println("slice1 is", slice1)

	// initializing a new slice from an existing slice
	var slice2 = slice1[0:3]
	fmt.Println("slice2 is", slice2)

	// slice given below refers to a newly initialized array
	var slice3 = []string{"a", "b", "c", "d", "e", "f"}
	fmt.Println("slice3 is", slice3)

	// declaring an empty slice
	var emptySlice1 = []int{}
	fmt.Println("emptySlice1 is", emptySlice1)

	// declaring an empty slice
	var emptySlice2 []int
	fmt.Println("emptySlice2 is", emptySlice2)
}

Program output

Above program produces following output:

array1 is [1 2 3 4 5 6 7 8]
slice1 is [1 2 3 4]
slice1 is [1 2 3 4 5 6]
slice2 is [1 2 3]
slice3 is [a b c d e f]
emptySlice1 is []
emptySlice2 is []

Program Description

As shown in above program, output of fmt.Println appears same for both arrays and slices however arrays and slices and are very different in Golang.

Declaring an empty or nil slice

Following lines in above demonstrates how to declare an empty slice:

var emptySlice1 = []int{}
var emptySlice2 []int

The type []T is a slice with elements of type T. In above program, the type of elements is int hence empty slice is initialized using the syntax []int.

Length and capacity of a nil slice or empty slice is 0.

Declaration of slices using indices

Following line in above program demonstrates how slices can be declared using indices to an existing array or existing slice

var slice1 = array1[0:4]
slice1 = slice1[0:6]

While declaring a slice from an existing slice or an array, following syntax is used: var new_slice = array_or_slice[start:end]

Hero, start can be any starting index of the previous array or slice, and end can be any index greater than start but less then the length of the underlying array or slice.

Declaring a new slice using slice literal

In above program, following line declares a new slice by providing new data.

// slice literal
var slice3 = []string{"a", "b", "c", "d", "e", "f"}

Slices are references to underlying arrays.

Above declaration works by creating a new array that contains above elements and the newly created slice references them.

Slice literals are like array literals but without the length.

// slice literal

[]int{1,2,3}

array literal:

[3]int{1,2,3}

Notice that array literals have length whereas slice literals do not have length.

Slices are references to array

Changing an element in a slice changes the elements in the underlying array because of this behaviour, all the slices that refer to elements of the underlying array are also changed.

The fact that slices are references to array is demonstrated in the program given below:

Program to demonstrate that slices are references to arrays

Program given below demonstrates how slices are references to arrays.

// program to demonstrate that slices are references to arrays
package main

import "fmt"

func main() {
	var arrayOfAlphabets = [4]string{
		"a",
		"b",
		"c",
		"d",
	}

	fmt.Println("arrayOfAlphabets is:", arrayOfAlphabets)

	var slice1 = arrayOfAlphabets[0:4]
	fmt.Println("slice1 is:", slice1)

	var slice2 = arrayOfAlphabets[0:4]
	fmt.Println("slice2 is:", slice2)

	// modifying slice1
	slice1[0]="w"
	slice1[1]="x"
	slice1[2]="y"
	slice1[3]="z"
	fmt.Println("slice1 is modified")

	// printing details of both slices
	fmt.Println("slice1 is:", slice1)
	fmt.Println("slice2 is:", slice2)
}

Program output

Above program produces following output:

arrayOfAlphabets is: [a b c d]
slice1 is: [a b c d]
slice2 is: [a b c d]
slice1 is modified
slice1 is: [w x y z]
slice2 is: [w x y z]

Program Description

When the slice1 is modified, slice2 gets modified as well because both slices are references to the same arrayOfAlphabets.

Other slices that share same underlying array will be altered as well.

Difference between slice literal and array literal

Program given below demonstrates the difference between a slice literal and array literal in Golang.

// program to demonstrate difference between a slice literal and array literal
package main

import "fmt"

func main() {
	// array literal
	var array1 = [3]string{"a", "b", "c"}

	// slice literal
	var slice1 = []string{"a", "b", "c"}

	// printing data 
	fmt.Println("array1 is:",array1)
	fmt.Println("slice1 is:",slice1)
}

Program output

Above program produces following output:

array1 is: [a b c]
slice1 is: [a b c]

Program to demonstrate default slice bounds

When an array is sliced, its default bounds must be difined to specify what elements must be present in the slice.

Program given below demonstrates how default slice bound works.

// program to demonstrate default slice bounds
package main

import "fmt"

func main() {
	var array1=[4]int{1,2,3,4}

	// equivalent expressions
	var slice1=array1[:]
	var slice2=array1[0:]
	var slice3=array1[:4]
	var slice4=array1[0:4]

	// printing data to console
	fmt.Println("slice1 is:",slice1)
	fmt.Println("slice2 is:",slice2)
	fmt.Println("slice3 is:",slice3)
	fmt.Println("slice4 is:",slice4)
}

Program output

Above program produces following output:

slice1 is: [1 2 3 4]
slice2 is: [1 2 3 4]
slice3 is: [1 2 3 4]
slice4 is: [1 2 3 4]

Program Description

As demonstrated in above program, the default slice bound is equivalent as various other expressions that are declared below the default slice bound expressions.

The default slice bound starts at zero and ends at length of the underlying array.

Program to demonstrate default slice bounds in Golang

Program given below demonstrates how custom slice bounds can be used for referring to certain elements in an array.

// program to demonstrate custom slice bounds
package main

import "fmt"

func main() {
	var array1 = [4]int{1, 2, 3, 4}

	fmt.Println("array1 is:", array1)
	// analyzing slice default bounds
	var slice1 = array1[0:1]
	var slice2 = array1[1:2]
	var slice3 = array1[2:3]
	var slice4 = array1[3:4]

	// printing data to console
	fmt.Println("slice1 is:", slice1)
	fmt.Println("slice2 is:", slice2)
	fmt.Println("slice3 is:", slice3)
	fmt.Println("slice4 is:", slice4)
}

Program output

Above program produces following output:

array1 is: [1 2 3 4]
slice1 is: [1]
slice2 is: [2]
slice3 is: [3]
slice4 is: [4]

Rules for defining slices bounds

  • Starting index of a slice bound can not be negative
  • Ending index of a slice bound can not be greater than the length of the underlying array.
  • Starting index of a slice bound must be lower than ending index of the slice bound.

Length and capacity of the slice

  • Length of the slice is the number of elements it contains in its bounds.
  • Capacity of the slice is the number of elements between the starting element in the slice to the ending element in the underlying array.
  • Re-slicing an existing slice can change both length and capacity of the slice.

Length of slices can be calculated by using the built in len function, similarly capacity of slices can be calculated by using the built in cap function.

Program given below demonstrates use of len and cap functions.

Length and capacity of nil slice on empty slices is zero.

Program to demonstrate calculating length and capacity of slice

Program given below demonstrates how to calculate length and capacity of slices using the built in len and cap functions.

// program to demonstrate custom slice bounds
package main

import "fmt"

func main() {
	var array1 = [4]int{1, 2, 3, 4}
	fmt.Println("array1 is:", array1)

	var slice1 = array1[0:4]

	fmt.Println("slice1 is:", slice1)

	// calculating length and capacity of slice1
	var lengthOfSlice1 = len(slice1)
	var capacityOfSlice1 = cap(slice1)

	fmt.Println("lengthOfSlice1 is:", lengthOfSlice1)
	fmt.Println("capacityOfSlice1 is:", capacityOfSlice1)

	slice1 = array1[3:4]
	fmt.Println("slice1 is re-sliced")

	// calculating length and capacity of slice2
	lengthOfSlice1 = len(slice1)
	capacityOfSlice1 = cap(slice1)

	fmt.Println("lengthOfSlice1 is:", lengthOfSlice1)
	fmt.Println("capacityOfSlice1 is:", capacityOfSlice1)
}

Program output

Above program produces following output:

array1 is: [1 2 3 4]
slice1 is: [1 2 3 4]
lengthOfSlice1 is: 4
capacityOfSlice1 is: 4
slice1 is re-sliced
lengthOfSlice1 is: 1
capacityOfSlice1 is: 1

Program Description

When an existing slice is re-sliced its length and capacity changes.

Program to demonstrate length and capacity of nil slices

Program given below demonstrates that length and capacity of nil slices is 0.

// program to demonstrate length and capacity of nil slice
package main

import "fmt"

func main() {
	// nil slice
	var slice1 []int
	fmt.Println("slice1 is:",slice1)

	fmt.Println("length of slice1 is:",len(slice1))
	fmt.Println("capacity of slice1 is:",cap(slice1))
}

Program output

Above program produces following output:

slice1 is: []
length of slice1 is: 0
capacity of slice1 is: 0

Subslicing

An existing slice can be sliced again using subslicing. Subslicing allows to change existing slice by specifying starting and ending indexes.

// program to demonstrate sub-slicing in Golang
package main

import "fmt"

func main() {
	// original slice of characters
	alphabets := []string{"a", "b", "c", "d", "e", "f"}
	fmt.Println("alphabets is ", alphabets)
	fmt.Println("alphabets[:2] is ", alphabets[:2])
	fmt.Println("alphabets[1:5] is ", alphabets[1:4])

	// create a sub-slice of characters
	subSlice := alphabets[:3]
	// print the new sub slice
	fmt.Println("subSlice is ", subSlice)
}

Program output

Above program produces following output:

alphabets :  [a b c d e f]
alphabets[:2] :  [a b]
alphabets[1:5] :  [b c d]
subSlice :  [a b c]