06 - Collections: Slices
📋 Jump to TakeawaysSo far, every variable holds one value. One number, one string, one boolean. But what if you need a list of names? Or a collection of scores? You're not going to make 50 separate variables. You need a list. In Go, that list is called a slice.
Arrays (Briefly)
Go has arrays, but they have a fixed size:
var scores [3]int
scores[0] = 10
scores[1] = 20
scores[2] = 30
fmt.Println(scores) // [10 20 30]The size is part of the type. A [3]int and a [4]int are different types. You can't add or remove elements. That's too rigid for most real work.
Slices
A slice is like an array that can grow and shrink. This is what you'll actually use:
scores := []int{10, 20, 30}
fmt.Println(scores) // [10 20 30]No number inside the brackets. That's what makes it a slice instead of an array.
Here we defined []int — a slice of integers. But slices work with any type: []string, []float64, []bool, or even []MyType for custom types you define yourself. We'll cover those later when we get to structs.
You can also start with an empty slice and add to it:
var scores []int
fmt.Println(scores) // []
fmt.Println(len(scores)) // 0append
append adds elements to a slice and returns a new slice:
names := []string{}
names = append(names, "Alice")
names = append(names, "Bob")
fmt.Println(names) // [Alice Bob]You must reassign the result: names = append(names, ...). append doesn't modify the original, it returns a new slice with the element added.
You can append multiple elements at once:
names = append(names, "Carol", "Dave")Accessing Elements
Elements are accessed by index, starting at 0:
names := []string{"Alice", "Bob", "Carol"}
fmt.Println(names[0]) // Alice
fmt.Println(names[1]) // Bob
fmt.Println(names[2]) // CarolAccessing an index that doesn't exist crashes the program:
fmt.Println(names[5]) // panic: index out of rangeAlways check len() before accessing by index if you're not sure.
len
len returns the number of elements:
names := []string{"Alice", "Bob"}
fmt.Println(len(names)) // 2Looping Over Slices
range gives you the index and value for each element:
names := []string{"Alice", "Bob", "Carol"}
for i, name := range names {
fmt.Printf("%d: %s\n", i, name)
}
// 0: Alice
// 1: Bob
// 2: CarolIf you only need the value:
for _, name := range names {
fmt.Println(name)
}Slicing a Slice
You can grab a portion of a slice with [start:end]:
nums := []int{10, 20, 30, 40, 50}
fmt.Println(nums[1:3]) // [20 30]
fmt.Println(nums[:2]) // [10 20]
fmt.Println(nums[3:]) // [40 50]start is inclusive, end is exclusive.
If start or end is beyond the slice's length, Go panics at runtime:
nums := []int{10, 20, 30, 40, 50}
fmt.Println(nums[:6]) // panic: slice bounds out of rangeRemoving an Element
Go doesn't have a built-in "remove" function. You combine slicing and append:
names := []string{"Alice", "Bob", "Carol"}
// Remove index 1 ("Bob")
names = append(names[:1], names[2:]...)
fmt.Println(names) // [Alice Carol]The ... unpacks the second slice so append can add each element individually.
Key Takeaways
- Arrays have a fixed size. Slices can grow and shrink. Use slices
[]string{}creates an empty slice.[]string{"a", "b"}creates one with valuesappend(slice, element)returns a new slice with the element added. Always reassignlen(slice)returns the number of elementsslice[index]accesses an element. Indexes start at 0rangeloops over a slice giving index and valueslice[start:end]creates a sub-slice. Start is inclusive, end is exclusive- Remove elements by combining
appendwith slicing:append(s[:i], s[i+1:]...)