Pills of Golang — Arrays and Slices

Alessio Trivisonno
4 min readMay 1, 2023

--

If you’re just getting started with Go, you may have heard about two similar-sounding data types called “arrays” and “slices”. At first glance, these two types may seem interchangeable, but they have some important differences that are worth understanding. In this post, we’ll take a closer look at arrays and slices in Go, and explain what sets them apart.

Arrays

An Array is a fixed-size collection of elements of the same type. Once an array is declared, its size cannot be changed.

var arr [5]int // this creates an array of 5 integers, with default values of 0

Slices

A Slice, on the other hand, is a dynamic, variable-length view into an array. A slice is created using the make() function, which takes two arguments: the element type and the initial size of the slice.

var s []int = make([]int, 5) // this creates a slice of 5 integers, with default values of 0

Slices are passed by reference, which means that any changes made to a slice will affect the underlying array:

Wait a second ? What do you mean by “underlying array” ?

A slice does not own the data it points to, it simply provides a view into an underlying array. This means that when a slice is passed to a function, it is passed by reference, and any changes made to the slice within the function will be reflected in the original slice. In contrast, when an array is passed to a function, it is passed by value, which means that any changes made to the array within the function will not be reflected in the original array.

But we said that Arrays are fixed-size and Slices are dynamic, then how can it be that slices are dynamic if under the hood they are both arrays ?

To understand that we need to understand another concept. In Go, a slice has both a length and a capacity. The length of a slice is the number of elements it currently contains, while the capacity of a slice is the number of elements it can potentially contain. The capacity of a slice is determined by the size of the underlying array that it points to. When a slice is created using the make() function, the capacity of the slice is set to the same value as its length.

However, opposite of arrays, you can increase the capacity of a slice using the append() function. When you append an element to a slice, Go checks if the slice has enough capacity to hold the new element. If the slice has enough capacity, the new element is added to the slice, and the length of the slice is increased by 1. If the slice does not have enough capacity, Go creates a new array with double the capacity of the old array, copies the elements from the old array to the new array, adds the new element to the new array, and updates the slice to point to the new array. This process is called a reallocation.

You can also explicitly set the capacity of a slice using the cap() function.

s := make([]int, 5, 10) // create a slice of length 5 and capacity 10

In this example, you can use the append() function to add elements to the slice up to its capacity of 10. Once you exceed the capacity of the slice, a reallocation will occur, and the capacity of the slice will be doubled to 20.

A tricky example

Let’s see now what happens if you assign an array ( or some elements of an array ) to a slice.

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // slice points to elements {2, 3, 4} of the array
slice[0] = 10 // modify the first element of the slice
fmt.Println(arr) // prints [1 10 3 4 5]

When you assign a slice from an array in Go, the slice and the array share the same underlying data. This means that if you modify the elements of the slice, the corresponding elements in the array will also be modified.

This behavior can be useful in some cases, such as when you want to create a subset of an existing array and modify the elements of that subset. However, it’s important to be aware of this to avoid unintentionally modifying the underlying data when working with slices.

If on the other hand, you want to create a copy of a slice without sharing the underlying data with the original array, you can use the copy() function to create a new slice and copy the elements from the original slice into the new slice. Here's an example:

arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // slice points to elements {2, 3, 4} of the array
copySlice := make([]int, len(slice))
copy(copySlice, slice) // copy the elements of slice into copySlice
copySlice[0] = 10 // modify the first element of copySlice
fmt.Println(arr) // prints [1 2 3 4 5], since the original array has not been modified

I hope you found this article interesting and if you wanna know more about the other subjects I am passionate about, like programming, cloud and SRE in general don’t hesitate to follow me on Medium for more stories like this one 😃 — Alessio Trivisonno

Cheers 🙌

--

--

Alessio Trivisonno
Alessio Trivisonno

Written by Alessio Trivisonno

SRE passionate about Cloud and Security

No responses yet