Golang Basics - Arrays and slices

Arrays and slices are used to store a collection of similarly typed items.

Arrays

In Go, arrays have a fixed length. If we wanted to store the name of all the models in a car showroom, we could use an array to do so:

// We can declare an array by stating its length
carModels := [3]string{"Toyota","Tesla","Ford"}

// Elements of the array can be accessed using the index
fmt.Println(carModels[1])
// "Tesla"

// Elements can be changed
carModels[2] = "BMW"

The length of an array is fixed: If we declare an array of 3 strings, it will always allocate and contain 3 strings, and this cannot be changed.

For most cases, we do not know the length of a collection beforehand, or we sometimes need to modify the size of the collection. For these purposes, we can use slices.

Slices

Slices are Go's answer to variable length arrays. Let's look at how we could implement the previous example using slices:

// We can declare an slice by ommiting the length parameter
carModels := []string{"Toyota","Tesla","Ford"}

// Elements are accessed in a similar way to arrays
fmt.Println(carModels[1])
// "Tesla"

// Elements can be changed
carModels[2] = "BMW"
// carModels = {"Toyota","Tesla","BMW"}

// We can add elements to a slice using the in-built `append` function
carModels = append(carModels, "Lexus")
// carModels = {"Toyota","Tesla","BMW", "Lexus"}

// We can get the length of the slice using the `len` function
fmt.Println(len(carModels))
// 4

It's important to note that slices, and arrays of every length are completely different in the eyes of the compiler. This means that []string, [3]string, and [10]string are three different types.

var arr1 [3]string
var arr2 [10]string
var slc1 []string

// All three of the below statements are invalid, and will throw a type mismatch error at the compile stage
fmt.Println(arr1 == arr2)
fmt.Println(arr2 == slc1)
fmt.Println(slc1 == arr1)

The value that a slice holds can be of any type, including structs, or even another slice:

type Vehicle struct{
        Model string
        Wheels int
}

var myVehicles []Vehicle
// `myVehicles` holds a collection of `Vehicle` types, which we can assign the same way as before
myVehicle = append(myVehicles, Vehicle{
        Model: "BMW",
        Wheels: 4,
})

var modelMatrix [][]string
// Here, `modelMatrix` is a slice of string slices

// To assign, each element has to be declared as a new slice
myMatrix = [][]string{
        []string{"m11", "m12", "m13"},
        []string{"m21", "m22", "m23"},
        []string{"m31", "m32", "m33"},
}

Slicing slices

We can get a subset of a slice when we need to extract specific elements:

allCarModels := []string{"Toyota","Tesla","Ford"}

someCarModels := allCarModels[1:3]
// This gives us all elements from index 1 till index 2 (not including index 3)
// someCarModels : {"Tesla", "Ford"}

someCarModels = allCarModels[1:]
// This gives us all elements from index 1 onwards
// someCarModels : {"Tesla", "Ford"}

someCarModels = allCarModels[:2]
// This gives us all elements from index 0 until (and not including) index 2
// someCarModels : {"Toyota", "Tesla"}

The type of a subslice is the same as the type of the slice. someCarModels and allCarModels have the same type: []string.

For a more advanced post on arrays, slices, and their internal working you can see the post on the official blog

📖 Read next: Control Flow - If, For, and Switch


Liked this article? Share it on: