# Golang - Chapter 11

Slices 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.

``a := []int{1, 2, 3, 4, 5}``

The above will first create an array of length 5 and then a slice with a reference to the array.

### Slicing

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.

``````func main() {
x := int{1,2,3,4,5}

y := x[:] // y = [1,2,3,4,5]
y = x[2:5] // y = [3,4,5]
y = x[2:] // y = [3,4,5]
y = x[:3] // y = [1,2,3,4]
}``````

Side Notes

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:

## Passing Slices to Functions

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.

``````func addOne(slice []int) {
for i := 0; i < len(slice); i++ {
slice[i] += 1
}
}

func main() {
originalArray := int{1, 2, 3, 4, 5}
slice := originalArray[2:] // contains [3 4 5]
fmt.Println(slice)	// prints [4 5 6]
fmt.Println(originalArray) // prints [1 2 4 5 6]
}``````

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 `originalArray`.

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 `slice`.

Side Notes

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.

### Length and Capacity of Slices

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 `cap(s)`, where `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.

``````func main() {
x := []int{1,2,3,4,5}

x = x[:0] // empty slice, len:0, cap:5
x = x[:4] // [1,2,3,4], len: 4, cap: 5
x = x[2:] // [3,4], len: 2, cap: 3. Cap is 3 as we count from 3 till 5 in the underlying array.
}``````

## Growing Slices

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)?

``````func growSlice(slice []int, value int) []int {
lengthOfSlice := len(slice)
slice = slice[0 : lengthOfSlice + 1]
slice[lengthOfSlice] = value
return slice
}

func main() {
var arr int // array of size 5
slice := arr[0:0]
for i := 0; i < 10; i++ {
slice = growSlice(slice, i)
fmt.Println(slice)
}
}``````

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.

``````
[0 1]
[0 1 2]
[0 1 2 3]
[0 1 2 3 4]
panic: runtime error: slice bounds out of range``````

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 `make` function.

### Using the `make` Function

We can also define a slice using the built-in `make([]T, len, cap) []T` function where `T` is the data type.

``````var s []byte
s = make([]byte, 5, 5)
// capacity defaults to the length if we omit it
s = make([]byte, 5) ``````

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.

``````s := make([]int, 0, 3)
for i := 0; i < 5; i++ {
s = append(s, i)
fmt.Printf("cap %v, len %v, %p\n", cap(s), len(s), s)
}

/*
Output
cap 3, len 1, 0x1040e130
cap 3, len 2, 0x1040e130
cap 3, len 3, 0x1040e130
cap 8, len 4, 0x10432220
cap 8, len 5, 0x10432220
*/``````

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.

### Appending to Slices

We often want to add new items to an existing slice, aka appending. In Go, we have the `append` method for this purpose.

``````func main() {
s := []int{1,2,3}
s = append(s, 4)
s = append(s, 5, 6, 7)
}``````

The `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.

### Zero Values

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.

### Two-Dimensional Slices

Slices can contain other slices. This is similar to creating 2-dimensional arrays in other languages.

We can do this by:

``````func main() {
arr := [][]int{
[]int{1,2,3,4}
[]int{5,6,7,8}
[]int{9,10,11,12}
}
}`````` I code.