# Working with slice in Go

Thanh Pham / Tue 24 Sep 2019

Slice in Go can be considered as dynamic version of array.

`[]T` is a slice of type `T`. Length and capacity of slice can be extended at run-time. This makes slice very flexible and widely used compare to array.

A new slice can  be created by using `make([]T, length, capacity),` slice literals or slicing from an existing array/slice:

``````// using make
s0 := make([]int, 2)
s1 := make([]int, 0, 5)
s2 := make([]int, 2, 5)

// slice literals
s3 := []float64{}
s4 := []float64{1.0, 2.0, 3.0}

// slicing from existing array
arr := int{1, 2, 3, 4, 5}
s5 := arr[0:1]
s6 := arr[:]
s7 := s6[2:3]
``````
Playground

Length is number of elements of the slice and capacity is number elements the underlying array can hold (see explain about underlying array in advanced section). And they can be calculated by using 2 built-in functions `len` and `cap`

``````s := make([]int, 0, 5)
fmt.Printf("len: %d, cap: %d", len(s), cap(s)) // len: 0, cap: 5``````

A new element can be added into a slice using `append` function:

``````s := make([]int, 0, 5)
s = append(s, 1)
s = append(s, 2, 3, 4, 5)
fmt.Printf("len: %d, cap: %d, values: %v", len(s), cap(s), s) // len: 5, cap: 5, values: [1 2 3 4 5]``````

Slice element can be accessed via index and range statement can be used to range over the slice:

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

// get value by index
fmt.Println("a:", a)

// modify value by index
a = 4
fmt.Println("a:", a)

// range over the slice
for i, v := range a {
fmt.Printf("index: %d, value: %d\n", i, v)
}
``````
Playground

A slice can be copied to another one by using `copy` or `append`:

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

// using copy
b := make([]int, len(a))
copy(b, a)

// using append
c := append([]int(nil), a...)

// or another way with append
d := append(a[:0:0], a...)
``````
Playground

`append` can be used to cut/delete 1 or more elements in the middle of a slice:

``````a := []int{1, 2, 3, 4, 5}
fmt.Println("a: ", a)

a = append(a[:1], a[2:]...) // cut the 2rd element
fmt.Println("a: ", a)

a = append(a[:1], a[3:]...) // cut the 2rd & 3rd element
fmt.Println("a: ", a)
``````
Playground

For more tricks of using slice, see: https://github.com/golang/go/wiki/SliceTricks

Slice can be considered as reference type. This means modifying its value inside a function will change the values of the original slice:

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

fmt.Println("a: ", a)
}

func changeValue(a []int, i int, v int) {
a[i] = v
}
``````
Playground

Capacity of slice will be double if its length reaches its current capacity: (more complex strategy if length of slice is greater than 1024, see slice.go)

``````a := make([]int, 0, 2)
a = append(a, 1, 2) // len: 2, cap: 2
a = append(a, 3) // len: 3, cap: 4
a = append(a, 4, 5) // len: 5, cap: 8
a = append(a, 6) // len: 6, cap: 8
``````
Playground

A slice is actually a composite type named `SliceHeader` which is composed of 3 words: `Data`, `Len` and `Cap`. Data is a pointer that point to the first element of the  underlying array that the slice can reach (not necessary the first element of the array). Len is number elements of the slice and Cap is length of the underlying array:

``````type SliceHeader struct {
Data uintptr
Len  int
Cap  int
}``````

A slice can be reverse back to `SliceHeader` by using `reflect` and `unsafe` package:

``````a := make([]int, 0, 5)
a = append(a, 1, 2)

fmt.Printf("Data: %d, Len: %d, Cap: %d", h.Data, h.Len, h.Cap) // Data: 824634388208, Len: 2, Cap: 5
``````
Playground

We can also reverse the pointer that point to the underlying array to an array using unsafe and type casting:

``````a := make([]int, 0, 5)
a = append(a, 1, 2)

arr := (*int)(unsafe.Pointer(h.Data))

fmt.Printf("%16s: %v\n", "slice", a) // [1 2]
fmt.Printf("%16s: %v\n", "underlying array", *arr) // [1 2 0 0 0]
``````
Playground

When a slice is passed into a function, a copied version of the `SliceHeader` is created, but the data is still the same:

``````func main() {
a := make([]int, 0, 5)
a = append(a, 1, 2)

// Address: 824633770032, Data: 824634212400, Len: 2, Cap: 5
fmt.Printf("Main - Address: %d, Data: %d, Len: %d, Cap: %d\n", unsafe.Pointer(&a), h.Data, h.Len, h.Cap)

printSliceInfo(a)
}

func printSliceInfo(a []int) {
// Address: 824633770056, Data: 824634212400, Len: 2, Cap: 5
fmt.Printf("Func - Address: %d, Data: %d, Len: %d, Cap: %d\n", unsafe.Pointer(&a), h.Data, h.Len, h.Cap)
}
``````
Playground

`append` statement always return a new slice header (as of Go 1.13):

``````a := make([]int, 0, 5)
a1 := append(a, 1, 2) // Address: 824633770032, Data: 824634212400, Len: 2, Cap: 5
a2 := append(a, 1, 2) // Address: 824634425392, Data: 824634441776, Len: 2, Cap: 5
``````
Playground

One of the reason `make` always return a new `SliceHeader` is because the underlying array will be re-allocated to a completely new one if length of slice reach its capacity:

``````a := make([]int, 0, 5)            // capacity = 5
a1 := append(a, 1, 2, 3, 4, 5, 6) // exceed capacity, new underlying array will be allocated.
``````
Playground

### Questions

What the below program print and why?

``````package main

import "fmt"

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

fmt.Println(arr)
}``````

What the below program print and why?

``````package main

import "fmt"

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

fmt.Println(arr)
fmt.Println(s)
}``````

What the below program print and why?

``````package main

import "fmt"

func main() {
arr := int{1, 2, 3, 4, 5}
s := arr[0:2]
s1 := arr[1:3]

s = append(s, 6)

fmt.Println(s)
fmt.Println(s1)
}``````