Arrays, Slices, and Maps
Go's core collection types — fixed-size arrays, the far more common slices, and key-value maps.
2 min read
Go gives you three built-in collection types, and in practice you'll use one of them constantly, one occasionally, and one almost never as a raw type.
Arrays: fixed size, rarely used directly
An array's length is part of its type and can't change:
var scores [5]int
scores[0] = 90
primes := [4]int{2, 3, 5, 7}[5]int and [4]int are genuinely different types — you can't pass one where the other is expected. Because of this rigidity, raw arrays show up rarely in everyday Go code; they mostly exist as the foundation slices are built on.
Slices: the collection type you'll actually use
A slice is a flexible, resizable view over an underlying array, and it's what Go code uses for "a list of things" in virtually every case:
fruits := []string{"apple", "banana", "cherry"}
fruits = append(fruits, "date")
fmt.Println(fruits) // [apple banana cherry date]
fmt.Println(len(fruits)) // 4Note the missing length in []string — that's what makes it a slice rather than an array. append returns a (possibly reallocated) slice, which is why you reassign the result back to fruits rather than mutating in place.
numbers := make([]int, 0, 10) // length 0, capacity 10make pre-allocates capacity when you know roughly how large a slice will grow, which avoids repeated reallocation as append grows it — worth doing in performance-sensitive code, though append(nil, ...)-style growth is perfectly fine for everyday use.
Slicing an existing slice (or array) takes a sub-view without copying the underlying data:
nums := []int{0, 1, 2, 3, 4, 5}
middle := nums[2:4] // [2 3] -- elements at index 2 up to (not including) 4Maps: key-value pairs
ages := map[string]int{
"Ada": 32,
"Grace": 45,
}
ages["Alan"] = 41 // add a new key
fmt.Println(ages["Ada"]) // 32Looking up a missing key doesn't panic — it returns the zero value for the map's value type, which can be genuinely ambiguous (is 0 a real age, or "not found"?). The two-value form resolves that:
age, ok := ages["Someone"]
if !ok {
fmt.Println("not found")
}ok tells you definitively whether the key existed, independent of what value came back. This "comma ok" idiom shows up everywhere maps are used and is worth internalizing early.
Deleting a key:
delete(ages, "Alan")Choosing between them
Reach for a slice by default any time you need an ordered collection — it's Go's equivalent of a list or array in other languages, and it's what 90% of collection-shaped problems call for. Reach for a map when you need fast lookup by key rather than by position. Reach for a raw array only when the size is fixed and known at compile time and you specifically want that rigidity (a 3D coordinate, a checksum's fixed byte count) — which, in practice, is rare enough that many Go developers go weeks without writing one.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.