todo Read https://go.dev/blog/slices-intro

It’s a dynamically-sized array. Much more commons than arrays.

Similar to Python’s list.

Init

var x = []int{10, 20, 30}
// With index values
var x = []int{1, 5: 4, 6, 10: 100, 15} // [1, 0, 0, 0, 0, 4, 6, 0, 0, 0, 100, 15]
// Multidimensional
var x [][]int

The type []T is a slice with elements of type T.

a[low : high]
 
primes := [6]int{2, 3, 5, 7, 11, 13} // array
var s []int = primes[1:4] // slice

Slice is a reference to an underlying array. Similar to Python. Changing the underlying array changes it for every slice that share the same reference.

Slice literals:

q := []int{2, 3, 5, 7, 11, 13}

Slice expressions. All produces the same result:

a[0:10]
a[:10]
a[0:]
a[:]

Slices can include other slices:

board := [][]string{
	[]string{"_", "_", "_"},
	[]string{"_", "_", "_"},
	[]string{"_", "_", "_"},
}

Length and capacity

Slice has a length and a capacity.

  • Length - number of elements the slice contains.
  • Capacity - number of elements in the underlying array counting from the first element in the slice (from start of the slice to the end of an array).

Example:

package main
 
import "fmt"
 
func main() {
	s := []int{2, 3, 5, 7, 11, 13}
	printSlice(s)
	// Slice the slice to give it zero length.
	s = s[:0]
	printSlice(s)
	// Extend its length.
	s = s[:4]
	printSlice(s)
	// Drop its first two values.
	s = s[2:]
	printSlice(s)
}
 
func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

Output is:

len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]

Nil

The zero value of a slice is nil. A nil slice has a length and capacity of 0 and has no underlying array.

package main
 
import "fmt"
 
func main() {
	var s []int
	fmt.Println(s, len(s), cap(s))
	if s == nil {
		fmt.Println("nil!")
	}
}

Output is:

[] 0 0
nil!

Init with make

a := make([]int, 5) // len=5 cap=5 [0 0 0 0 0]
b := make([]int, 0, 5) // len=0 cap=5 []
c := b[:2] // len=2 cap=5 [0 0]
d := c[2:5] // len=3 cap=3 [0 0 0]

If you know approximately how many items will be in the slice, init it with the cap value specified - it will improve the performance.

Never specify a capacity that’s less than the length! It is a compile-time error to do so with a constant or numeric literal. If you use a variable to specify a capacity that’s smaller than the length, your program will panic at runtime.

Capacity that is < length is a compile-time error in Go. If specified with a variable and < than length, the program will Go panic at runtime.

Append

Use built-in append function to append new elements to a slice. Slice will grow as needed.

var x []int // len=0 cap=0 []
x = append(x, 0) // len=1 cap=1 [0]
x = append(x, 1) // len=2 cap=2 [0 1]
x = append(x, 2, 3, 4) // len=5 cap=6 [0 1 2 3 4]

If there are no more room for the new elements, Go will allocate more memory to the underlying array, sometimes with some additional extra space than needed for the current operation.

When a slice grows via append, it takes time for the Go runtime to allocate new memory and copy the existing data from the old memory to the new. The old memory also needs to be garbage collected. For this reason, the Go runtime usually increases a slice by more than one each time it runs out of capacity. The rule as of Go 1.18 is to double the capacity of a slice when the current capacity is less than 256. A bigger slice increases by (current_capacity + 768)/4. This slowly converges at 25% growth (a slice with capacity of 512 will grow by 63%, but a slice with capacity 4,096 will grow by only 30%).

With a spread(q Is it called like that in Go?) operator ... you can append an entire slice:

y := []int{20, 30, 40}
x = append(x, y...)

Compare

Slices aren’t comparable.

The only thing you can compare a slice with using == is nil:

fmt.Println(x == nil) // "true"

To compare slices a slices package from the standard library has:

slices.Equal()
slices.EqualFunc()

The reflect package contains a function called DeepEqual that can compare almost anything, including slices. It’s a legacy function, primarily intended for testing. Before the inclusion of slices.Equal and slices.EqualFunc, reflect.DeepEqual was often used to compare slices. Don’t use it in new code, as it is slower and less safe than using the functions in the slices package. Learning Go

Emptying

As of Go 1.21 a clear() function can be used to set all slice elements to their zero values.

s := []string{"first", "second", "third"}
fmt.Println(s, len(s)) // [first second third] 3
clear(s)
fmt.Println(s, len(s)) // [  ] 3

Copy

Use built-in copy(). It will copy as much elements from the source as possible up to its length.

x := []int{1, 2, 3, 4}
y := make([]int, 4)
num := copy(y, x)
fmt.Println(y, num) // [1 2 3 4] 4
 
// Will copy only the first 2 elements
x := []int{1, 2, 3, 4}
y := make([]int, 2)
num := copy(y, x)
 
// Copy from the middle
copy(y, x[2:])

It’s also possible to copy slices between the same underlying slice:

x := []int{1, 2, 3, 4}
// Copy the last three values in x on top of the first three values of x
num := copy(x[:3], x[1:])
fmt.Println(x, num) // [2 3 4 4] 3

Convert to array

Explicit type conversion required:

xSlice := []int{1, 2, 3, 4}
xArray := [4]int(xSlice)
smallArray := [2]int(xSlice) // [1 2]
panicArray := [5]int(xSlice) // panic, size can't be bigger than slice len
xArrayPointer := (*[4]int)(xSlice) // memory will be shared

The size of the array must be specified at compile time. It’s a compile-time error to use [...] in a slice to array type conversion.

References