ZetCode

Go slice

last modified August 17, 2021

Go slice tutorial shows how to work with slices in Golang.

An array is a collection of elements of a single data type. An array holds a fixed number of elements, and it cannot grow or shrink. Elements of an array are accessed through indexes.

A slice is a dynamically-sized, flexible view into the elements of an array. A slice can grow and shrink within the bounds of the underlying array. A slice does not store any data, it just describes a section of the array.

Go declare slice

var s []T

We declare a slice having type T. The slice is declared just like an array except that we do not specify any size in the brackets [].

Go slice literal

A slice can be created with a slice literal.

literal.go
package main

import "fmt"

func main() {
    var s1 = []int{2, 5, 6, 7, 8}

    s2 := []int{3, 5, 1, 2, 8}

    fmt.Println("s1:", s1)
    fmt.Println("s2:", s2)
}

In the example, we create two slices.

var s1 = []int{2, 5, 6, 7, 8}

The expression on the right-hand side is a slice literal.

s2 := []int{3, 5, 1, 2, 8}

Here we have a short-hand equivalent.

$ go run literal.go
s1: [2 5 6 7 8]
s2: [3 5 1 2 8]

Go slice make function

We can use the make built-in function to create new slices in Go.

func make([]T, len, cap) []T

The make function takes a type, a length, and an optional capacity. It allocates an underlying array with size equal to the given capacity, and returns a slice that refers to that array.

make_fun.go
package main

import "fmt"

func main() {

    vals := make([]int, 5)

    fmt.Println("vals: ", vals)

    vals[0] = 1
    vals[1] = 2
    vals[2] = 3
    vals[3] = 4
    vals[4] = 5

    fmt.Println("vals: ", vals)
}

We create a slice of integer having size 5 with the make function. Initially, the elements of the slice are all zeros. We then assign new values to the slice elements.

$ go run make_fun.go
vals:  [0 0 0 0 0]
vals:  [1 2 3 4 5]

Go slice length and capacity

The len function returns the number of elements in the slice. The cap function returns the capacity of the slice. The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.

len_cap.go
package main

import "fmt"

func main() {

    vals := make([]int, 5, 10)

    n := len(vals)
    c := cap(vals)

    fmt.Printf("The size is: %d\n", n)
    fmt.Printf("The capacity is: %d\n", c)

    vals2 := vals[0:4]
    n2 := len(vals2)
    c2 := cap(vals2)

    fmt.Printf("The size is: %d\n", n2)
    fmt.Printf("The capacity is: %d\n", c2)
}

In the example, we print the size and the capacity of two slices.

$ go run len_cap.go
The size is: 6
The capacity is: 6
The size is: 4
The capacity is: 6

Go slicing an array or slice

We can create a slice by slicing an existing array or slice.

To form a slice, we specify a low bound and a high bound: a[low:high]. This selects a half-open range which includes the first element, but excludes the last.

We can omit the high or low bounds to use their defaults instead. The default is zero for the low bound and the length of the slice for the high bound.

slicing.go
package main

import "fmt"

func main() {

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

    s1 := vals[1:4]
    fmt.Printf("s1: %v, cap: %d\n", s1, cap(s1))

    s2 := vals[5:7]
    fmt.Printf("s2: %v, cap: %d\n", s2, cap(s2))

    s3 := vals[:4]
    fmt.Printf("s3: %v, cap: %d\n", s3, cap(s3))

    s4 := vals[2:]
    fmt.Printf("s4: %v, cap: %d\n", s4, cap(s4))

    s5 := vals[:]
    fmt.Printf("s5: %v, cap: %d\n", s5, cap(s5))
}

We create slices from an array of integers.

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

An array of integers is created. With the ... operator, Go calculates the size of the array for us.

s2 := vals[5:7]
fmt.Printf("s2: %v, cap: %d\n", s2, cap(s2))

We create a slice from the vals array. The resulting slice contains elements starting from index 5 up to index 7; the upper bound is non-inclusive.

$ go run slicing.go
s1: [2 3 4], cap: 6
s2: [6 7], cap: 2
s3: [1 2 3 4], cap: 7
s4: [3 4 5 6 7], cap: 5
s5: [1 2 3 4 5 6 7], cap: 7

Go slice iteration

With for loops, we can iterate over slice elements in Go.

iteration.go
package main

import "fmt"

func main() {

    words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"}

    for idx, word := range words {

        fmt.Println(idx, word)
    }
}

In the example, we iterate over a slice of words with for/range statements.

$ go run iteration.go
0 falcon
1 bold
2 bear
3 sky
4 cloud
5 ocean

In the following example, we iterate a slice with a classic for loop.

iteration2.go
package main

import "fmt"

func main() {

    words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"}

    for i := 0; i < len(words); i++ {

        fmt.Println(words[i])
    }
}

We iterate over a slice of words with for statement.

$ go run iteration2.go
falcon
bold
bear
sky
cloud
ocean

Go slice append

The built-in append function appends new elements to the slice.

func append(s []T, vs ...T) []T

The first parameter is a slice of type T, and the rest are T values to append to the slice.

The resulting value of append is a slice containing all the elements of the original slice plus the provided values. If the backing array is too small to fit all the given values, a bigger array will be allocated. The returned slice will point to the newly allocated array.

appending.go
package main

import "fmt"

func main() {

    vals := make([]int, 3)

    fmt.Printf("slice: %v; len: %d; cap: %d \n", vals, len(vals), cap(vals))

    fmt.Println("---------------------------")

    vals = append(vals, 1)
    vals = append(vals, 2)
    vals = append(vals, 3)
    vals = append(vals, 4, 5, 6)

    fmt.Printf("slice: %v; len: %d; cap: %d \n", vals, len(vals), cap(vals))
}

In the example, we append new elements to a slice, which already has three elements.

vals := make([]int, 3)

First, we create a slice having three elements initiated to 0.

vals = append(vals, 1)
vals = append(vals, 2)
vals = append(vals, 3)
vals = append(vals, 4, 5, 6)

We append six values to the slice. Multiple elements can be appended in one go.

$ go run appending.go
slice: [0 0 0]; len: 3; cap: 3
---------------------------
slice: [0 0 0 1 2 3 4 5 6]; len: 9; cap: 12

Under the hood, Go enlarged the underlying array to include all the new elements.

Go slice copy

The built-in copy function copies a slice.

func copy(dst, src []T) int

The function returns the number of elements copied.

copying.go
package main

import "fmt"

func main() {

    vals := []int{1, 2, 3, 4, 5}

    vals2 := make([]int, len(vals))

    n := copy(vals2, vals)

    fmt.Printf("%d elements copied\n", n)

    fmt.Println("vals:", vals)
    fmt.Println("vals2:", vals2)
}

In the example, we copy a slice of integers.

$ go run copying.go
5 elements copied
vals: [1 2 3 4 5]
vals2: [1 2 3 4 5]

Go slice remove element

There is no built-in function to remove items from a slice. We can do the deletion with the append function.

remove_elements.go
package main

import (
    "fmt"
)

func main() {

    words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"}
    fmt.Println(words)

    words = append(words[1:2], words[2:]...)
    fmt.Println(words)

    words = append(words[:2], words[4:]...)
    fmt.Println(words)
}

In the example, we delete an element and then two elements from the slice.

words = append(words[1:2], words[2:]...)

This removes the first element from the slice. We accomplish the removal by appending two slices omitting the one to be deleted.

$ go run remove_elements.go
[falcon bold bear sky cloud ocean]
[bold bear sky cloud ocean]
[bold bear ocean]

Go slice unique elements

In the next example, we generate a slice with unique elements.

uniq_elems.go
package main

import "fmt"

func uniq(vals []int) []int {

    uvals := []int{}
    seen := make(map[int]bool)

    for _, val := range vals {

        if _, in := seen[val]; !in {

            seen[val] = true
            uvals = append(uvals, val)
        }
    }

    return uvals
}


func main() {

    vals := []int{1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 9, 9}
    uvals := uniq(vals)

    fmt.Printf("Original slice: %v\n", vals)
    fmt.Printf("Unique slice: %v\n", uvals)
}

We have a slice with duplicate elements. We create a new slice that contains only unique elements.

seen := make(map[int]bool)

To accomplish this task, we create a map that stores a boolean true for all values that we encounter in the slice.

for _, val := range vals {

    if _, in := seen[val]; !in {

        seen[val] = true
        uvals = append(uvals, val)
    }
}

We go over the elements of the slice with possible duplicates. If it is not present in the seen map, we store it there and append it to the new uvals slice. Otherwise, we skip the if block.

$ go run uniq_elems.go
Original slice: [1 2 2 3 4 4 5 6 7 8 8 8 9 9]
Unique slice: [1 2 3 4 5 6 7 8 9]

Go slice sorting

Go contains the sort package to sort slices.

sorting.go
package main

import (
    "fmt"
    "sort"
)

func main() {

    words := []string{"falcon", "bold", "bear", "sky", "cloud", "ocean"}
    vals := []int{4, 2, 1, 5, 6, 8, 0, -3}

    sort.Strings(words)
    sort.Ints(vals)

    fmt.Println(words)
    fmt.Println(vals)
}

In the example, we sort a slice of words and integers.

$ go run sorting.go
[bear bold cloud falcon ocean sky]
[-3 0 1 2 4 5 6 8]

Go slice initial value

The default zero value of a slice is nil. The nil slice has a length and capacity of 0 and has no underlying array.

When we create a slice with the make function, all elements are initialized to 0.

initial.go
package main

import "fmt"

func main() {

    var vals []int

    if vals == nil {
        fmt.Printf("slice is nil\n")
    }

    fmt.Printf("slice: %v; len: %d; cap: %d \n", vals, len(vals), cap(vals))

    fmt.Println("---------------------------")

    var vals2 = make([]int, 5)
    fmt.Printf("slice: %v; len: %d; cap: %d \n", vals2, len(vals2), cap(vals2))
}

We create two slices with a default zero value and make-function initialized elements.

$ go run initial.go
slice is nil
slice: []; len: 0; cap: 0
---------------------------
slice: [0 0 0 0 0]; len: 5; cap: 5

Go slice is reference type

A slice is a reference type in Go. This means that when we assign a reference to a new variable or pass a slice to a function, the reference to the slice is copied.

value_type.go
package main

import "fmt"

func main() {

    vals := []int{ 1, 2, 3, 4, 5, 6 }
    vals2 := vals

    vals2[0] = 11
    vals2[1] = 22

    fmt.Println(vals)
    fmt.Println(vals2)
}

In the example, we define a slice and assign the slice to a new variable. The changes made through the second variable are reflected in the original slice.

$ go run reftype.go
[11 22 3 4 5 6]
[11 22 3 4 5 6]

The original slice is also modified.

Go slice of slices

Go slice can also contain other slices.

sliceofslices.go
package main

import "fmt"

func main() {

    words := [][]string{
        {"sky", "ocean"},
        {"red", "blue"},
        {"C#", "Go"},
    }

    fmt.Printf("slice: %v; len: %d; cap: %d \n", words, len(words), cap(words))
}

The example creates a slice of slices.

$ go run sliceofslices.go
slice: [[sky ocean] [red blue] [C# Go]]; len: 3; cap: 3

Go filter slice of structs

In the next example, we filter a slice of Go structures.

filter_slice_structs.go
package main

import "fmt"

type User struct {
    name       string
    occupation string
    country    string
}

func main() {

    users := []User{

        {"John Doe", "gardener", "USA"},
        {"Roger Roe", "driver", "UK"},
        {"Paul Smith", "programmer", "Canada"},
        {"Lucia Mala", "teacher", "Slovakia"},
        {"Patrick Connor", "shopkeeper", "USA"},
        {"Tim Welson", "programmer", "Canada"},
        {"Tomas Smutny", "programmer", "Slovakia"},
    }

    var programmers []User

    for _, user := range users {

        if isProgrammer(user) {
            programmers = append(programmers, user)
        }
    }

    fmt.Println("Programmers:")
    for _, u := range programmers {

        fmt.Println(u)
    }
}

func isProgrammer(user User) bool {

    return user.occupation == "programmer"
}

In the example, we define a slice of users. We create a new slice that contains only programmers.

type User struct {
    name       string
    occupation string
    country    string
}

The User struct has three fields.

users := []User{

    {"John Doe", "gardener", "USA"},
    {"Roger Roe", "driver", "UK"},
    {"Paul Smith", "programmer", "Canada"},
    {"Lucia Mala", "teacher", "Slovakia"},
    {"Patrick Connor", "shopkeeper", "USA"},
    {"Tim Welson", "programmer", "Canada"},
    {"Tomas Smutny", "programmer", "Slovakia"},
}

This is the original slice of User structures.

var programmers []User

The filtered users/programmers are stored in the programmers slice.

for _, user := range users {

    if isProgrammer(user) {
        programmers = append(programmers, user)
    }
}

We go over the users slice and add a current user to the programmers slice only if the user satisfies the isProgrammer predicate.

func isProgrammer(user User) bool {

    return user.occupation == "programmer"
}

The IsProgrammer predicate returns true for all users whose occupation field equals to "programmer".

$ go run filter_slice_structs.go
Programmers:
{Paul Smith programmer Canada}
{Tim Welson programmer Canada}
{Tomas Smutny programmer Slovakia}

In this tutorial, we have worked with slice in Golang.

List all Go tutorials.