A slice is a core data structure in Go. Let's learn more about them .
Welcome to another bite-sized lesson. In this video we're going to talk about slices which are a fundamental data structure in Go. Slices stand out as one of the most versatile and widely used data structures in Go because they provide a dynamic and flexible view into arrays, which makes them essential for managing data. If you're new to Go or you're looking to just understand a little bit more about slices, by the end of this video you'll know everything you need to know.
So as we mentioned, slices are a little lightweight abstraction over the top of arrays in Go. Unlike arrays, slices are dynamically sized, which allows them to shrink and grow. This enables a more powerful way to work with a sequence of elements, enabling really easy data manipulation without the rigidity that comes with fixed-size arrays. So let's take a look at how to make these.
Firstly, let's take a simple example and look at how to make a fixed-size array—a slice when we know all the elements in it. So we make a slice of type integers: say we want to add 1, 2, 3, 4, and 5 to it and then print it out. If we run this, I think it's going to do exactly what you'd expect: it prints 1, 2, 3, 4, 5 to the console.
Now let's do this a little bit differently. If you've watched my video on maps, you'll know we use a different syntax: the make
syntax. So let's try this one too. For example:
fruits := make([]string, 3, 5)
Then we add some fruits and print it out. As you can see, we use a different syntax, but it hasn't made a meaningful difference to how we use it.
One interesting thing: if we do format.Printf
to print the length and capacity—len(fruits)
and cap(fruits)
—we'll see the length is 3 but the capacity is 5, meaning we can fit 5 items in it. If we try to directly assign to fruits[3]
, we'll get an index-out-of-range error since the length is only 3. Instead, we use append
, which can expand the slice size for us.
As we keep appending, Go automatically increases the slice's capacity. If we add enough items, you'll see the capacity doubles to accommodate them. If you want to put an item at the front (like another "apple"), we might use a trick with append
by making a small slice []string{"apple"}
and then spreading out the rest, effectively prepending.
Slices can also be used to make sub-slices. For example:
people := []string{"Jack", "John", "Jacob", "Jill"}
. We can do people[1:4]
to skip index 0, or people[:3]
, or people[2:]
, depending on which part of the slice we want. This is super handy for slicing up data.
Iterating over slices is straightforward: you can use for _, person := range people
to print each item. Or you can use a classic for-loop with indexes if you're more comfortable with that style.
Finally, copy
creates a shallow copy of a slice. If you just do copy(copyPeople, people)
, you'll have separate underlying data. But if you do copyPeople = people
, you'll end up referencing the same backing array, meaning changes in one slice affect the other. You have to be careful to use copy
if you truly want separate data. That's everything you need to get started with slices! I hope it's helpful and you now know all the warnings about using the copy function.