Go
Slices

Slices and arrays

Slices wrap arrays to give a more general, powerful, and convenient interface to sequences of data.

Slices hold references to an underlying array

Defining array:

var arr [5]int

Defining slice:

var slice []int

Slices in memory and how they work with hardware

  • RAM is just a mapping of addresses to data
  • slices and the arrays that they are built on top of are contiguously stored in memory instead of randomly, which is good for performance
  • however, when growing a slice whose underlying array is not large enough, the underlying array is reallocated to a new point in RAM and the slice is adjusted to point to the new array (copying data from memory to memory is not efficient)

Make

Most of the time we don't have to think about the underlying array of a slice, just create new slice using make function

// func make([]T, len, cap) []T
mySlice := make([]int, 5, 10)
 
// the capacity argument is usually omitted and defaults to the length
mySlice := make([]int, 5)
 
// creating slice w specific set of values using slice literal
mySlice := []string{"I", "love", "go"}

Length and capacity

Length of a slice is simply the number of elements it contains.

Capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice

mySlice := []string{"I", "love", "go"}
fmt.Println(len(mySlice)) // 3
 
mySlice := []string{"I", "love", "go"}
fmt.Println(cap(mySlice)) // 3

Generally speaking, unless you're hyper-optimizing the memory usage of your program, you don't need to worry about the capacity of a slice because it will automatically grow as needed.

Variadic

Many functions, especially those in the standard library, can take an arbitrary number of final arguments. This is accomplished by using the "..." syntax in the function signature.

A variadic function receives the variadic arguments as a slice

func concat(strs ...string) string {
    final := ""
    // strs is just a slice of strings
    for i := 0; i < len(strs); i++ {
        final += strs[i]
    }
    return final
}
 
func main() {
    final := concat("Hello ", "there ", "friend!")
    fmt.Println(final)
    // Output: Hello there friend!
}

The Spread operator allows us to pass a slice into a variadic function

func printStrings(strings ...string) {
	for i := 0; i < len(strings); i++ {
		fmt.Println(strings[i])
	}
}
 
func main() {
    names := []string{"bob", "sue", "alice"}
    printStrings(names...)
}

Append

append() built in function that is used to dynamically add ele to slices

  • The append() function changes the underlying array of its parameter AND returns a new slice
    • append() on anything other that itself is usually a bad idea
  • if the underlying array is not large enough, it will be reallocated to a new point in memory and point the slice to it
slice = append(slice, oneThing)
slice = append(slice, firstThing, secondThing)
slice = append(slice, anotherSlice...)