Arrays & Slices
Arrays and slices are fundamental data structures in Go. While arrays have a fixed size, slices are dynamic and flexibleโmaking them the most commonly used collection type. In this lesson, you'll master both and learn when to use each.
Arrays
An array is a fixed-size sequence of elements of the same type:
Click Run to execute your code
- Fixed size - Size is part of the type
- Value type - Copying creates a new array
- Zero-indexed - First element is at index 0
- Initialized to zero values by default
Array Declaration
// Declare with size
var arr [5]int // [0 0 0 0 0]
// Declare and initialize
numbers := [5]int{1, 2, 3, 4, 5}
// Let compiler count the size
numbers := [...]int{1, 2, 3, 4, 5} // Size is 5
// Initialize specific indices
arr := [5]int{0: 10, 2: 20, 4: 30} // [10 0 20 0 30]
[3]int and [5]int are completely different types.
Slices
Slices are dynamic, flexible views into arrays. They're the most common way to work with sequences in Go:
Click Run to execute your code
- Dynamic size - Can grow and shrink
- Reference type - Points to underlying array
- Three components - Pointer, length, capacity
- Most commonly used collection in Go
Creating Slices
// Slice literal
numbers := []int{1, 2, 3, 4, 5}
// Using make (length and capacity)
slice := make([]int, 5) // length 5, capacity 5
slice := make([]int, 5, 10) // length 5, capacity 10
// From an array
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // [2 3 4]
// Empty slice
var slice []int // nil slice
Length and Capacity
Slices have both length and capacity:
| Property | Function | Description |
|---|---|---|
| Length | len(slice) |
Number of elements in the slice |
| Capacity | cap(slice) |
Number of elements in underlying array (from first element) |
slice := make([]int, 5, 10)
fmt.Println(len(slice)) // 5 (length)
fmt.Println(cap(slice)) // 10 (capacity)
// Slicing affects length but not capacity
s := []int{1, 2, 3, 4, 5}
s2 := s[1:3] // [2 3]
fmt.Println(len(s2)) // 2
fmt.Println(cap(s2)) // 4 (from index 1 to end of array)
Appending to Slices
The append function adds elements to a slice:
Click Run to execute your code
- If capacity is sufficient, append adds to existing array
- If capacity is exceeded, a new larger array is allocated
- Elements are copied to the new array
- The slice is updated to point to the new array
Append Patterns
// Append single element
slice = append(slice, 6)
// Append multiple elements
slice = append(slice, 7, 8, 9)
// Append another slice (use ...)
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}
slice1 = append(slice1, slice2...) // [1 2 3 4 5 6]
slice = append(slice, value). The underlying array may change!
Slice Operations
Slicing Syntax
s := []int{0, 1, 2, 3, 4, 5}
s[1:4] // [1 2 3] - from index 1 to 3
s[:3] // [0 1 2] - from start to index 2
s[3:] // [3 4 5] - from index 3 to end
s[:] // [0 1 2 3 4 5] - entire slice
// With capacity
s[1:3:5] // [1 2] with capacity 4
Copy Function
Click Run to execute your code
copy returns the number of elements
copied, which is
the minimum of len(dst) and len(src).
Arrays vs Slices: When to Use Which?
| Feature | Array | Slice |
|---|---|---|
| Size | Fixed | Dynamic |
| Type | Value type | Reference type |
| Passing to functions | Copies entire array | Copies slice header (cheap) |
| Common usage | Rare (fixed-size data) | Very common |
| Can grow? | No | Yes (with append) |
Figure: Arrays vs Slices - Understanding fixed-size arrays and dynamic slices with their internal structure
Common Mistakes
1. Not assigning append result
// โ Wrong - append result not assigned
slice := []int{1, 2, 3}
append(slice, 4) // Doesn't modify slice!
fmt.Println(slice) // [1 2 3]
// โ
Correct
slice = append(slice, 4)
fmt.Println(slice) // [1 2 3 4]
2. Slice sharing underlying array
// โ Unexpected - slices share array
original := []int{1, 2, 3, 4, 5}
slice1 := original[0:3]
slice2 := original[2:5]
slice1[2] = 99
fmt.Println(slice2) // [99 4 5] - affected!
// โ
Correct - use copy for independence
original := []int{1, 2, 3, 4, 5}
slice1 := make([]int, 3)
copy(slice1, original[0:3])
slice1[2] = 99
fmt.Println(original) // [1 2 3 4 5] - unchanged
3. Comparing slices with ==
// โ Wrong - can't compare slices
s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
if s1 == s2 { // Error: invalid operation
// ...
}
// โ
Correct - compare manually or use reflect.DeepEqual
func equal(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
Exercise: Slice Manipulation
Task: Create functions to manipulate slices.
Requirements:
- Create a function
filterthat removes even numbers - Create a function
reversethat reverses a slice - Test with slice: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- Print original, filtered, and reversed
Show Solution
package main
import "fmt"
// filter removes even numbers from slice
func filter(numbers []int) []int {
result := []int{}
for _, num := range numbers {
if num%2 != 0 { // Keep odd numbers
result = append(result, num)
}
}
return result
}
// reverse reverses a slice
func reverse(numbers []int) []int {
result := make([]int, len(numbers))
for i, num := range numbers {
result[len(numbers)-1-i] = num
}
return result
}
// reverseInPlace reverses a slice in place
func reverseInPlace(numbers []int) {
for i := 0; i < len(numbers)/2; i++ {
j := len(numbers) - 1 - i
numbers[i], numbers[j] = numbers[j], numbers[i]
}
}
func main() {
original := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("Original:", original)
// Filter even numbers
filtered := filter(original)
fmt.Println("Filtered (odd only):", filtered)
// Reverse
reversed := reverse(original)
fmt.Println("Reversed:", reversed)
// Bonus: Reverse in place
nums := []int{1, 2, 3, 4, 5}
reverseInPlace(nums)
fmt.Println("Reversed in place:", nums)
}
Summary
- Arrays have fixed size and are value types
- Slices are dynamic and reference underlying arrays
- Slices have length and capacity - use len() and cap()
- append adds elements, may allocate new array
- copy copies elements between slices
- Slicing syntax: s[low:high] or s[low:high:max]
- Always assign append result back to the slice
- Use slices for almost everything (not arrays)
What's Next?
Now that you understand slices, you're ready to learn about MapsโGo's built-in hash table type for key-value pairs. Maps are essential for many programming tasks!
Enjoying these tutorials?