Skip to content

Go (Golang) 5 - Arrays & Slices

Published:

In this tutorial, we’ll explore arrays and slices. You’ll learn about their use cases, key differences, when to choose one over the other, and practical ways to use them.

Understanding Arrays and Slices in Go

Arrays

Like most languages, Go has arrays. While you’ll often use slices, it’s important to understand arrays because slices use them under the hood and they have their own use cases.

An array is a sequence of elements that share the same type. They are fixed in size, meaning the length of the array is defined when you create it and cannot be changed.

Declaring arrays

Here are some ways you can declare an array in Go.

var zeroArray [5]int

This will create an array with a length of 5. Since we did not specify any values Go will set all elements to zero values which is zero for int. If we had done the same for string the elements in the array would have been empty strings.

If you already know what values you want you can specify it like this.

var myArray = [3]int {5, 10, 15}

If we don’t wanna set all values we can choose to specify at what index we wanna set them.

var cars = [5]string{1: "Volvo", 4: "BMW"}

You can replace the number that specifies the size of the array with three dots. It will then determine the length of the array based on the number of elements provided

var snacks = [...]string{"chips", "popcorn", "peanuts"}

If we print the values of the arrays we created we will get the following output

fmt.Println(zeroArray) // 0, 0, 0, 0, 0
fmt.Println(myArray) // 5, 10, 15
fmt.Println(cars) // "", "Volvo", "", "", "BMW"
fmt.Println(snacks) // chips, popcorn, peanuts

Get the length of an array

To get the length of an array you can use the built-in function len

fmt.Println(len(snacks)) // 3

Comparing arrays

You can also compare two arrays.

x := [3]int {1, 2, 3}
y := [3]int {1, 2, 3}

fmt.Println(x == y) // true
fmt.Println(x != y) // false

Arrays will be equal if they have the same length, contain the same elements, and are in the same order.

Updating elements in an array

To update elements in an array you can use this syntax.

x[0] = 10
fmt.Println(x[0]) // 10
fmt.Println(x) // 10, 2, 3

Name of the array followed by brackets and the index you want to update and set a new value.

If you try to target an index that does not exist Go will panic and you will get an error.

If you know the length ahead of time, you can use arrays. However, if the length is uncertain, it’s better to use slices

Slices

Slices can change in size, allowing you to add or remove elements. Operations like append, copy, and slicing subsets are common with slices, providing a versatile tool for managing collections of data

Declaring slices

Slices look very similar to arrays but you don’t specify a size.

someSlice := []int{5,10,15}

Same as with arrays you can can specify what indexes you wanna set.

var carsSlice = []string{1: "Volvo", 4: "BMW"} // ["", "Volvo", "", "", "BMW"]

When declaring a slice without giving any values it will be set to a zero value and for slices that is nil. In Go, nil is used to represent that something doesn’t have any value assigned to it yet.

var sliceWithNoValue []int

Updating elements in a slice

Reading values values works the same as arrays. You can also update a value by index.

someSlice[2] = 100
fmt.Println(someSlice[2]) // 100

Comparing slices

You can’t compare slices like we did with arrays. You can only compare it with nil

fmt.Println(sliceWithNoValue == nil) // true
fmt.Println(sliceWithNoValue != nil) // false

To check if one slice is equal to another slice you have to use the Equal function

fruits1 := []string{"apple", "banana", "peach"}
fruits2 := []string{"apple", "banana", "peach"}

fmt.Println(slices.Equal(fruits1, fruits2)) // true

You can only use the Equal function on slices that are of the same type.

Get the length of slices

To check the length of a slice we can use the len function same as with arrays.

fmt.Println(len(fruits1)) // 3
fmt.Println(len(sliceWithNoValue)) // 0

if len is used on a nil slice it will return 0.

Appending values to a slice

To add values to a slice, you can use the append function. This function doesn’t modify the original slice. Instead, it returns a new slice that includes the added values, which you must assign to capture the changes.

fruits1 = append(fruits1, "coconut")
fmt.Println(fruits1) // apple, banana, peach, coconut

It’s also possible to append multiple values at a time.

fruits1 = append(fruits1, 'strawberry, raspberry')
fmt.Println(fruits1) // apple, banana, peach, coconut, strawberry, raspberry

To append the values of another slice you can use the ellipsis operator

var fruits3 = []string{"durian", "dragonfruit"}
fruits1 = append(fruits3...)
fmt.Println(fruits1) // apple, banana, peach, coconut, strawberry, raspberry, durian, dragonfruit

This will unpack fruits3 and pass all elements as arguments.

How slices grow

Slices in Go are built on arrays, giving them a capacity that defines how many elements they can hold before needing to resize. For example, if a slice with a capacity of 3 requires a fourth element, Go creates a larger array and transfers the elements to it. The resizing rules are as follows.

• For small slices (fewer than 1024 elements), capacity doubles. • For larger slices (1024 elements or more), capacity increases by about 25% each time.

To check the capacity of a slice in go you can use the cap function. Let’s also see how Go increases the capacity of the slice.

var nums []int
fmt.Println(nums, len(nums), cap(nums)) // [] 0 0
nums = append(nums, 1)
fmt.Println(nums, len(nums), cap(nums)) // [1] 1 1
nums = append(nums, 2)
fmt.Println(nums, len(nums), cap(nums)) // [1 2] 2 2
nums = append(nums, 3)
fmt.Println(nums, len(nums), cap(nums)) // [1 2 3] 3 4
nums = append(nums, 4)
fmt.Println(nums, len(nums), cap(nums)) // [1 2 3 4] 4 4
nums = append(nums, 5)
fmt.Println(nums, len(nums), cap(nums)) // [1 2 3 4 5] 5 8

As you can see every time we add an element and exceed the capacity Go increases it (in this case doubling it).

If you know the capacity you need for a slice, you can use the make function to set it upfront. This helps optimize your code.

nums2 := make([]int, 0, 20) // Set the length to 0 & capacity to 20
nums2 = append(nums2, 5, 10, 15, 20) // nums2 will have a length of 4 & capacity of 20

This way, appending elements doesn’t require resizing the slice, because it already has the necessary capacity.

Slicing slices

to get a subset of a slice (this can be used on arrays too) you can use something called slicing

nums3 := []int{2,4,8,16,32}
fmt.Printl(nums3[1:4]) // 4, 8 16

The number to the left of the colon indicates the starting position and includes that element. The number to the right is the end position but excludes that element, which is why the result is 4, 8, 16.

You can also omit the start and end index.

fmt.Println(nums3[:4])
fmt.Println(nums3[1:])
fmt.Println(nums3[:])

Be aware of that when using slicing it will not create a copy of the elements it will still use the same reference.

nums4[0] = 1337

fmt.Println(nums3) // 2,1337,8,16,32
fmt.Println(nums4) // 1337,8,16

When modifying nums4 it will also update nums3 since they are using the same array under the hood.

Removing an item from a slice

Now that you understand how slicing works, you can remove an element from a slice by adjusting its positions and using the append function. Since there’s no built-in function for removing elements, this must be done manually. Here’s an approach to remove an element at a specific position.

s := []int{10, 20, 30, 40}
s = append(s[:2], s[2+1:]...)
fmt.Println(s) // 10, 20, 40,

This code removes the third element (30) from the slice containing [10, 20, 30, 40]. It does this by slicing the elements before and after the third element and combining them with append. The slice [10, 20, 40] is printed, confirming that 30 has been successfully removed.

Copying a slice

If you want to create a copy you can use the copy function. You pass the destination as the first argument and the source slice to copy from as the second argument.

values := []int{5,6,7,8}
newValues := make([]int, 4)
copy(newValues, values)
newValues[0] = 1337
fmt.Println(newValues) // 1337, 6 ,7 ,8
fmt.Println(values)  // 5, 6 ,7 ,8

To copy all the values of the first slice the second slice needs to at least have the same length as the first slice if it does not all values will not be copied over.

When we update newValues, values will stay the same.

Conclusion

Full source code can be found here Github

Choosing between arrays and slices depends on your needs. Arrays are simple and fast for fixed-size collections, while slices offer flexibility for dynamic data. With this tutorial, you should now have a deeper understanding of how to effectively use them.