DEV Community

Cover image for Slicing Slices in GO
Azeez Lukman
Azeez Lukman

Posted on

Slicing Slices in GO

Slices wrap arrays in Go and provide a more general, powerful, and convenient interface to data sequences. In this article, you will cover slice basics such as creating, initializing, and iteration. You will also learn how to grow a slice, work with subsets of slices, and slice tricks.

Slices contain a list of elements of a particular type, but unlike arrays, there are methods available to add or remove elements from the slice. This is because as the name implies, slices don't hold any actual data themselves. A slice provides a view into the elements of the underlying array and provides extra methods for interacting with the data.

slices are much more common than arrays

Slice Type

The type []T denotes a slice of type T for example:

[]string
Enter fullscreen mode Exit fullscreen mode

This says hey, this is a slice of strings or this slice contains string values. The values would be comma separated. This is similar to how you would declare the type for an array variable, except the length is omitted here.

For the most part, functions for working with slices is identical to functions for working with arrays. These functions includes ones for accessing elements, using zero values, passing slices to the len function, and for...range loops.

Slice Values

Like i mentioned above, slices themselves don't store any values at all. You may be wondering how it then holds data. Slices are designed to work as a convenience wrapper for arrays, allowing you to easily perform functions that would have been difficult to manually perform directly on an array.

I like to think of a slice as a peek into some section of the underlying array. That peek is where you interact with the array from and the data you peek into is what you get to operate on.

Now, you should note that modifying the peek elements makes the actual changes into the underlying array directly. This could be a blessing and a curse at the same time since the other slices with the same underlying array would see the changes.

Declaring slices

To declare the type for a variable that holds a slice, you use an empty pair of square brackets, followed by the type of elements the slice will hold.

var intSlice []int
Enter fullscreen mode Exit fullscreen mode

This only declares a slice type that holds integer values as intSlice. Declaring a slice variable doesn’t automatically create a slice. For that, you call the built-in make function. You pass make the type of the slice you want to create and the length of slice it should create and assign it to your slice variable.

var intSlice []int
intSlice = make([]int, 5)
Enter fullscreen mode Exit fullscreen mode

After declaring the slice variable intSlice . The next line creates the actual slice using the data type and the length. Notice the data type passed into make function is the same as that of the slice variable.

So the make method created the slice, but hold, what about the values? By default the make method would fill the slice with the underlying data type's zero value of the slice we created for the length we passed in. In our example the slice intSlice now contains 5 values as 0

You can now use the builtin slice methods, let's run a few on the newly created slice.

len(intSlice) //4
intSlice[3] // 0
Enter fullscreen mode Exit fullscreen mode

Next, we want to fill those spots up with our actual data, you would assign its elements using the same syntax you used for accessing it.

intSlice[0] = 10 
intSlice[1] = 8
intSlice[2] = 9 
intSlice[3] = 12

fmt.Println(inSlice[0])  // 10
fmt.Println(inSlice[1])  // 8
fmt.Println(inSlice)  // 10, 8, 9, 12, 0, 0
Enter fullscreen mode Exit fullscreen mode

Slice literal

When you know the values that goes into your slice before declaring, you can directly pass them in using slice literal. With slice literal, you do not need to use the make function, your slice gets declared and assigned its values in one step

A slice literal looks just like an array literal, except the length is omitted:

stringSlice := []string{"hello","world"}
Enter fullscreen mode Exit fullscreen mode

You can even use a multi line slice literal to make your more organized

stringSlice := []string{
         "hello",
         "world",
             "once",
         "more",
}
Enter fullscreen mode Exit fullscreen mode

When you use a slice literal, an array is created and the slice is created to reference it, the slice gets a peek of the whole value inserted by the slice literal. The only way to access the array created is through the slice.

Slicing arrays

Using the slice operator, you can create a slice directly from the array without using the make function, the operator is similar to the one used for accessing and assigning values in slices, but it has two indexes instead, the first denotes the start of the slice and the other denotes the end, excluding that index. The elements peeked into would span from the first index up to but not including the second index.

arr = [4]{1, 2, 4, 8}
arrSlice = arr[1:3]
fmt.Println(arrSlice) // 2,4
Enter fullscreen mode Exit fullscreen mode

omitting the last index causes the slice to include vlalues through to the last index of the array

arr = [4]{1, 2, 4, 8}
arrSlice = arr[1:]
fmt.Println(arrSlice) // 2,4,
Enter fullscreen mode Exit fullscreen mode

The os.Args package variable contains a slice of strings with the command-line arguments the current program was run with

Add items onto a slice

Go provides a built-in append function that takes a slice, and one or more values you want to append to the end of that slice and returns a new, larger slice with all the same elements as the original slice, with the new element(s) added onto the end.

slice = []int{10, 5, 8}
append(slice, 20)
append(slice, 20)
Enter fullscreen mode Exit fullscreen mode

A slice’s underlying array can’t grow in size. If there isn’t room in the array to add elements, A new, larger array would be created and all its elements will be copied into it, then the slice will be updated to refer to this new array. But since all this happens behind the scenes in the append function, there’s no easy way to tell whether the slice returned from append has the same underlying array as the slice you passed in, or a different underlying array. If you keep both slices, this can lead to some unpredictable behavior such as inconsistencies.

To avoid this, Always make sure to assign the return value of append function back to the same slice variable we passed to append

slice = []int{10, 5, 8}
slice = append(slice, 20)
slice = append(slice, 20)
Enter fullscreen mode Exit fullscreen mode

Summary

Slices are a convenience wrapper around arrays, this is why they are very similar to arrays even with the methods used in manipulating them. Slices don't store value but operates on the underlying array. They can only hold one data type just like arrays too.

Other things to explore includes converting strings to slices and vice versa, you should also look into how slices are used in place of variadic arguments in variadic functions.

Thank you for reading, i'm Azeez Lukman, should you need any help or questions please feel free to reach out @robogeek95 everywhere

Discussion (0)