Slices are abstractions based on arrays which offer more flexibility. Slices are not arrays. Instead a slice describes a piece of an array.
Defining slices are same as defining array, except that we don't specify the element count.
The above will first create an array of length 5 and then a slice with a reference to the array.
As mentioned, we can define slices using the high and low bounds, though they are optional. This is similar to how slices work in Python. The following example shows how we can create various slices from an array.
Slicing does not copy the slice's data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices. Therefore, modifying the elements (not the slice itself) of a re-slice modifies the elements of the original slice:
Though slices contain a pointer to an array, they are not a pointer themselves. Instead, slices are structs which hold a pointer and a length, and later we will see the third property, capacity. Hence, when we pass slices to a function, a copy of the slice is created.
However, when we access an index of the slice within the function, we are actually accessing the index of the original array. Any modifications made to this index, will affect the original slice's values as well.
In the above
main() function, we called
addOne(), which adds 1 to each element in a slice. This call created a copy of
slice and passed it to
addOne(). However, when we accessed the index of the slice within
addOne(), we were actually referencing the contents of
As we incremented each element of the slice, we had actually incremented each index pointing to
originalArray. Therefore, printing
slice shows that the elements have indeed been incremented by one. Also,
originalArray has been modified at the indexes pointed to by
If you wanted to modify the original slice without creating a copy of it, you can create a function which accepts a pointer to the slice instead.
There are 2 attributes of a slice: length and capacity.
We can obtain the length of a slice by calling
len(s) and capacity by calling
s is the slice.
The length here refers to the number of elements the slice contains.
Capacity refers to the number of elements present in the array that the slice is pointing to (remember that slices actually are pointing to an array!). Capacity starts counting from the first element in the slice.
Now that we know the difference between length and capacity, what happens if we try to grow a slice beyond its capacity (i.e. the original array's capacity)?
In the above example, we have
growSlice() which is a function, that creates a new slice that has a new length which is one larger than before. Note that we had defined the array that the slice points to in the
main() function, which has a capacity of 5.
This is the result we end up with: panic.
When we try to grow a slice beyond the referenced array's capacity, we encounter a panic, or runtime error
slice bounds out of range.
Does this mean we can never grow a slice beyond the original capacity? Of course we can! For that, we have the
We can also define a slice using the built-in
make(T, len, cap) T function where
T is the data type.
We can create a slice by slicing an array or another slice. Similar to Python, we define the part of the array to slice e.g.
l := s[2:5]. We can also slice the whole array by
s := x[:].
We can append to a slice. When we reach the capacity of the slice, a copy of the array is made, with a new capacity (i.e. contiguous memory) of twice the previous capacity is allocated.
As you can see once the capacity is met, append will return a new slice with a larger capacity. On the 4th iteration you will notice a larger capacity and a new pointer address.
We often want to add new items to an existing slice, aka appending. In Go, we have the
append method for this purpose.
append method returns a slice containing all the original values together with the newly appended values. Also, if the underlying array of the slice is too small to hold the new element, a bigger array is allocated.
A slice's zero value is
nil. Internally as a struct, it has a length of 0, capacity of 0, and does not have an array to point to.
Slices can contain other slices. This is similar to creating 2-dimensional arrays in other languages.
We can do this by: