Slices in Golang: [14 Easy Examples]

In this tutorial, we will learn about slices in Golang using 14 easy examples. In Go, both array and slices are used to store elements based on different scenarios. Array has certain limitations like:

  • Array has fixed size
  • Array size must be specified in the declaration
  • Array holds the actual elements and so on

Because of these limitations of array, we use more powerful and dynamic data structure called slices in Golang. Let us understand what slices are and what all operations we can do using slices with examples in the next section.

 

Slices Overview

Slices are fundamental data structure in Golang that provides a more flexible way to work with variable length of sequences of elements. Slices stores only similar type of elements. Working with slices looks quite a bit like working with arrays, but there are subtle differences. The first thing to notice is that we do not specify the size of the slice when we declare it unlike array, where we specify the size when declaring the array. Syntax for slices:

var x []int

This syntax creates a slice of integers. ‘[]’ denotes that the size of the slice is dynamic and keep on expanding as we go on adding the elements to the slice. Since no value is assigned, x is assigned the zero value for a slice. This is called nil slice. In Go, nil is not same as null. nil is an identifier that represents the lack of a value for some types. nil has no type, so it can be assigned to or compared against values of different types.

 

var x = []int{<elem1>, <elem2>, <elem3>}

This syntax creates a slice of 3 integers.

 

NOTE:

Using […] makes an array. Using [] makes a slice.

 

Slices in Golang: [14 Easy Examples]

 

Slices in Golang: [14 Easy Examples]

Also read: Go Run vs Go Build: Explained with Example

Creating Slices

There are multiple ways to create a slice. Two most common ways are either to use var keyword or slice literal.

var x = []int{50, 60, 80}

Example-1: This creates a slice of 3 integer using a slice literal. Just like array, we can also specify only the indices with values in the slice literal.

package main

import (
    "fmt"
)

func main() {

    var x = []int{50, 60, 80}
    fmt.Println(x)
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\create-slice.go
[50 60 80]

x := []int{1, 4: 110, 6, 8: 50, 10}

Example-2: This creates a slice of 12 integers using var keyword with the following values: [1, 0, 0, 0, 110, 6, 0, 0, 50, 10]

package main

import (
    "fmt"
)

func main() {

    var x = []int{1, 4: 110, 6, 8: 50, 10}
    fmt.Println(x)
}

OUTPUT

PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\create-slice.go
[1 0 0 0 110 6 0 0 50 10]

NOTE:

Slices are non comparable data type which means it is compile-time error to use == to see if two slices are identical or != to see if they are different. The only thing we can compare a slice with is nil as shown below:
fmt.Println(x == nil)   //prints true

 

Read and Write Slices

We read and write slices using bracket syntax as shown below. Just like array, we can  not read or write past the end or use a negative index.

Example-3: In below example, we have created a slice from which we are reading the value at index 2 and writing the value at the end of the slice.

package main

import (
    "fmt"
)

func main() {

    var x = []int{1, 4: 110, 6, 8: 50, 10}
    fmt.Println("Reading from the Slice: ", x[2])

    y := append(x, 90)
    fmt.Println("Writing to the Slice: ", y)
}

OUTPUT

PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\read-write-slice.go
Reading from the Slice: 0
Writing to the Slice: [1 0 0 0 110 6 0 0 50 10 90]

 

Example-4: If we try to read from the slice using negative index, we will get below error.

package main

import (
    "fmt"
)

func main() {

    var x = []int{1, 4: 110, 6, 8: 50, 10}
    fmt.Println("Reading from the Slice: ", x[-1])
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\read-write-slice.go
# command-line-arguments
.\read-write-slice.go:9:44: invalid argument: index -1 (constant of type int) must not be negative

 

Length of Slices

We use built-in function called len to find out the length of slices, similar to array. Note that when we pass nil slice to len function, it returns the size as zero.

Example-5: In the below example, we have created a slice and priting its length using len function.

package main

import (
    "fmt"
)

func main() {

    var x = []int{1, 4: 110, 6, 8: 50, 10}
    fmt.Println("Size of slice: ", len(x))
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-length.go
Size of slice: 10

 

Capacity of Slices

Also read: CPU Bound Processes in Golang [4 Best Examples]

In Go, each element of slice is assigned to consecutive memory locations which makes it quick to read and write these values. Capacity is defined as the number of consecutive memory location reserved for a slice. This can be larger than the length of slice. When we append additional elements to a slice, the moment length becomes equal to  the capacity of slice , there is no room left for any additional elements.

In such case, Go runtime allocates a new slice with a large capacity and values in original slice are copied to the new slice. The new elements are now copied to the end of new slice and new slice is returned. The capacity of a slice is find out using built-in function called cap.

Example-6: In below example, notice how the capacity of slice is getting expanded when we keep on adding the elements to it.

package main

import (
    "fmt"
)

func main() {

    var x = []int{}
    fmt.Println(x, len(x), cap(x))

    x = append(x, 10)
    fmt.Println(x, len(x), cap(x))

    x = append(x, 12, 30)
    fmt.Println(x, len(x), cap(x))

    x = append(x, 9)
    fmt.Println(x, len(x), cap(x))
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-capacity.go
[] 0 0
[10] 1 1
[10 12 30] 3 3
[10 12 30 9] 4 6

 

We can also create a slice of definite capacity using a built-in function called make. Example:

  1. var x = make([]int, 10)
  2.  var x = make([]int, 5 , 10)

First option creates a slice of integers having length and capacity both 10. Second option again creates a slice of integers but here the length is 5 and capacity is 10.

 

Appending to Slices

To grow the length of slices, we use built-in function called append. The append function takes at least two parameters, a slice of any type and a value of that type. It returns the slice of the same type.

Example-7: In the below example, we are adding two elements (9,10) at the end of slice x.

package main

import (
    "fmt"
)

func main() {

    var x = []int{4, 5, 2, 8}
    x = append(x, 9, 10)
    fmt.Println("New Slice after append: ", x)
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-append.go
New Slice after append: [4 5 2 8 9 10]

 

We can also append one slice onto another by using the … operator.

Example-8: In this example, we are adding slice y at the end of slice x.

package main

import (
    "fmt"
)

func main() {

    var x = []int{20, 30}
    var y = []int{80, 90, 10}

    x = append(x, y...)
    fmt.Println("New Slice after append: ", x)
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-append.go
New Slice after append: [20 30 80 90 10]

 

Slicing Slices

A slice expression creates a slice from a slice. It is written inside brackets and consists of a starting offset and an ending offset, separated by a (:)  just like we do string slices in Python.

Example-9: In below example, we are slicing the slice x in different ways.

package main

import (
    "fmt"
)

func main() {

    x := []int{10, 20, 30, 40, 50}
    a := x[:2]
    b := x[1:]
    c := x[1:3]
    d := x[:]

    fmt.Println("a: ", a)
    fmt.Println("b: ", b)
    fmt.Println("c: ", c)
    fmt.Println("d: ", d)
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-slicing.go
a: [10 20]
b: [20 30 40 50]
c: [20 30]
d: [10 20 30 40 50]

 

Copying Slices

To copy one slice to another, we use a built-in function called copy. It takes two parameters, first is destination slice and the second is the source slice. It is not necessary to always copy the entire slice, we can copy few elements also to destination slice.

Example-10: In the below example, we have created slice x of 3 integers and slice y of type integers whose length is 4 and capacity is 5. In the output, it returns total number of elements copied which is 3 in this case along with new slice after copy.

Copy entire slice

package main

import (
    "fmt"
)

func main() {

    x := []int{10, 20, 30}
    y := make([]int, 4, 5)

    num := copy(y, x)
    fmt.Println("Number of elements copied:", num)
    fmt.Println("New slice after copy:", y)
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-copy.go
Number of elements copied: 3
New slice after copy: [10 20 30 0]

 

Copy middle elements from the source slice

Example-11: In below example, we are copying elements at index 2 & 3 of slice x to slice y.

package main

import (
    "fmt"
)

func main() {

    x := []int{10, 20, 30, 40, 12, 23, 13}
    y := make([]int, 4, 5)

    num := copy(y, x[2:4])
    fmt.Println("Number of elements copied: ", num)
    fmt.Println("New slice after copy: ", y)
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-copy.go
Number of elements copied: 2
New slice after copy: [30 40 0 0]

 

Iterating Slices

We can use for-range loop like any other programming language to iterate over the slices.

Example-12: In below example, we have created integer slice x which has 5 elements. We iterate over this slice and prints its elements using for-range loop.

package main

import "fmt"

func main() {

    x := []int{10, 20, 30, 40, 50}

    for index, elem := range x {
        fmt.Printf("Index: %d , Element: %d\n", index, elem)
    }
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-iterate.go
Index: 0 , Element: 10
Index: 1 , Element: 20
Index: 2 , Element: 30
Index: 3 , Element: 40
Index: 4 , Element: 50

 

Passing Slices to Functions

Slices are passed by value, but the value being passed is a reference to the underlying array.

Example-13:  In below example, we are passing slice x to the function modifySlice and replacing the element  at index zero to value 100.

package main

import (
    "fmt"
)

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

func main() {

    var x = []int{10, 20, 30}
    modifySlice(slice)
    fmt.Println(slice)  
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\slice-func.go
[100 20 30]

 

Multidimensional Slices

When we create a slice whose elements too are slices, it is known as multidimensional slices.

Example-14: In below example, we have created multi dimensional slice of type integer having two elements.

package main

import "fmt"

func main() {

    x := [][]int{
        {1, 2, 3},
        {4, 5, 6},
    }
    fmt.Println("Multidimensional Slice", x)
}
OUTPUT
PS C:\Users\linuxnasa\OneDrive\Desktop\Go-Dump> go run .\multi-dim-slice.go
Multidimensional Slice [[1 2 3] [4 5 6]]

 

Summary

Slices are essential data structure in Golang due to its flexibility and memory efficiency. They provide a versatile way to work with collections of data while maintaining the benefits of arrays.

Leave a Comment