Slices: Arrays That Can Grow!
Welcome back! Arrays are great, but they have one big problem: they can’t grow. Once you create an array with 5 boxes, you can never have 6 boxes.
Enter Slices! Slices are like magical arrays that can grow and shrink as needed. Think of them as expandable shopping bags instead of fixed-size boxes.
Arrays vs Slices (Real Life Analogy)
Fixed Array = Fixed Parking Lot
- Exactly 10 parking spaces
- Can’t add more spaces
- Can’t remove spaces
- Perfect for when you know exactly how many cars you have
Slice = Expandable Backpack
- Starts with some space
- Automatically grows when you add more items
- Can shrink when you remove items
- Perfect for shopping lists that change
What is a Slice?
A slice is like a window into an array. It shows you only part of the array, but you can move and resize that window.
Think of it like this:
- Array = A long shelf of books
- Slice = A bookmark that shows you “books 3 through 7”
Creating Slices
Method 1: From an Array (Like Cutting a Cake)
// Start with a full cake (array)
fullCake := [8]string{"🍰", "🍰", "🍰", "🍰", "🍰", "🍰", "🍰", "🍰"}
// Take a slice (piece) of the cake
middlePiece := fullCake[2:6] // Pieces 2, 3, 4, 5 (4 pieces)
// Take the first half
firstHalf := fullCake[:4] // Pieces 0, 1, 2, 3
// Take the second half
secondHalf := fullCake[4:] // Pieces 4, 5, 6, 7
// Take everything
wholeCake := fullCake[:] // All pieces
Real Life Example:
// Your weekly schedule
week := [7]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
// Work days only
workDays := week[:5] // ["Mon", "Tue", "Wed", "Thu", "Fri"]
// Weekend only
weekend := week[5:] // ["Sat", "Sun"]
Method 2: Creating Empty Slices (Empty Shopping Bag)
// Empty slice that can hold strings
var shoppingList []string
// Empty slice with initial space (bag with some pockets)
var todoList = make([]string, 0, 10) // Can hold up to 10 items initially
// Slice with starting items
fruits := []string{"apple", "banana"} // Starts with 2 items
Adding Items to Slices (The Magic!)
Using append() - Like Adding to Your Shopping Bag
// Start with empty shopping list
var shopping []string
// Add items one by one
shopping = append(shopping, "milk")
shopping = append(shopping, "bread")
shopping = append(shopping, "eggs")
// Now shopping = ["milk", "bread", "eggs"]
// Add multiple items at once
shopping = append(shopping, "apples", "oranges", "bananas")
// Now shopping = ["milk", "bread", "eggs", "apples", "oranges", "bananas"]
Important: Always use shopping = append(shopping, "item") because append returns a new slice!
Real Life Example: Building a Playlist
package main
import "fmt"
func main() {
// Start with empty playlist
var playlist []string
fmt.Println("=== Building My Playlist ===")
// Add songs one by one
playlist = append(playlist, "Song 1")
fmt.Printf("Added: %v\n", playlist)
playlist = append(playlist, "Song 2")
fmt.Printf("Added: %v\n", playlist)
playlist = append(playlist, "Song 3", "Song 4")
fmt.Printf("Added more: %v\n", playlist)
fmt.Printf("Total songs: %d\n", len(playlist))
}
Output:
=== Building My Playlist ===
Added: [Song 1]
Added: [Song 1 Song 2]
Added more: [Song 1 Song 2 Song 3 Song 4]
Total songs: 4
Understanding Length vs Capacity
Slices have two important measurements:
Length = How many items you have
Capacity = How much space you have
// Create a slice with space for 10 items, but start with 0 items
var numbers = make([]int, 0, 10)
fmt.Printf("Length: %d, Capacity: %d\n", len(numbers), cap(numbers))
// Length: 0, Capacity: 10
// Add 3 numbers
numbers = append(numbers, 1, 2, 3)
fmt.Printf("Length: %d, Capacity: %d\n", len(numbers), cap(numbers))
// Length: 3, Capacity: 10
Think of it like:
- Length = How many seats are occupied in a theater
- Capacity = Total seats available in the theater
Reading from Slices
Accessing Items (Same as Arrays)
fruits := []string{"apple", "banana", "orange", "grape"}
first := fruits[0] // "apple"
second := fruits[1] // "banana"
last := fruits[3] // "grape"
Looping Through Slices
scores := []int{85, 92, 78, 90, 88}
fmt.Println("=== All Scores ===")
for i, score := range scores {
fmt.Printf("Test %d: %d points\n", i+1, score)
}
Modifying Slices
Changing Items
colors := []string{"red", "blue", "green"}
// Change blue to purple
colors[1] = "purple"
// Now colors = ["red", "purple", "green"]
Real Life Example: Updating a To-Do List
package main
import "fmt"
func main() {
// Start with tasks
tasks := []string{"buy milk", "clean room", "do homework"}
fmt.Println("Original tasks:", tasks)
// Update a task
tasks[1] = "clean kitchen"
// Add new tasks
tasks = append(tasks, "call mom", "exercise")
fmt.Println("Updated tasks:", tasks)
fmt.Printf("Total tasks: %d\n", len(tasks))
}
Copying Slices
Making a Copy (Like Photocopying a List)
original := []int{1, 2, 3, 4, 5}
// Method 1: Create destination first
copy1 := make([]int, len(original))
copy(copy1, original)
// Method 2: Use append (creates a copy)
copy2 := append([]int(nil), original...)
// Method 3: Slice notation (shares memory!)
copy3 := original[:] // This shares the same underlying array!
Warning: copy3 := original[:] shares the same memory. If you change copy3, original changes too!
Common Slice Operations
Removing Items
func removeItem(slice []string, index int) []string {
// Remove item at index by combining parts before and after
return append(slice[:index], slice[index+1:]...)
}
// Example
fruits := []string{"apple", "banana", "orange", "grape"}
fruits = removeItem(fruits, 1) // Remove "banana"
// Now fruits = ["apple", "orange", "grape"]
Inserting Items
func insertItem(slice []string, index int, item string) []string {
// Make space at index
slice = append(slice[:index+1], slice[index:]...)
// Put item in the space
slice[index] = item
return slice
}
// Example
colors := []string{"red", "blue", "green"}
colors = insertItem(colors, 1, "yellow")
// Now colors = ["red", "yellow", "blue", "green"]
Finding Items
func findItem(slice []string, target string) int {
for i, item := range slice {
if item == target {
return i // Found at this position
}
}
return -1 // Not found
}
// Example
fruits := []string{"apple", "banana", "orange"}
bananaPos := findItem(fruits, "banana") // Returns 1
grapePos := findItem(fruits, "grape") // Returns -1
Real Life Example: Shopping Cart
package main
import "fmt"
func main() {
// Start with empty cart
var cart []string
// Add items
cart = append(cart, "bread", "milk", "eggs")
fmt.Println("Initial cart:", cart)
// Add more items
cart = append(cart, "apples", "bananas")
fmt.Println("Added fruits:", cart)
// Check if we have milk
hasMilk := false
for _, item := range cart {
if item == "milk" {
hasMilk = true
break
}
}
if hasMilk {
fmt.Println("✓ You have milk!")
}
// Remove eggs (let's say we don't need them)
for i, item := range cart {
if item == "eggs" {
cart = append(cart[:i], cart[i+1:]...)
break
}
}
fmt.Println("Final cart:", cart)
fmt.Printf("Total items: %d\n", len(cart))
}
Output:
Initial cart: [bread milk eggs]
Added fruits: [bread milk eggs apples bananas]
✓ You have milk!
Final cart: [bread milk apples bananas]
Total items: 4
Slice Gotchas (Watch Out!)
The Reference Problem
original := []string{"a", "b", "c"}
copy := original[:] // This shares memory!
copy[0] = "z" // This changes BOTH slices!
fmt.Println("Original:", original) // ["z", "b", "c"] - Changed!
fmt.Println("Copy:", copy) // ["z", "b", "c"]
Solution: Use copy() function or append() to make real copies.
Capacity Confusion
slice := make([]int, 3, 10) // Length 3, Capacity 10
slice = append(slice, 1) // Length becomes 4, still capacity 10
// But if you add 8 more items...
for i := 0; i < 8; i++ {
slice = append(slice, i)
}
// Now Go might create a new underlying array with more capacity
Practice Time!
Exercise 1: Grade Book
Create a slice to store student grades. Add grades, calculate average, find highest grade.
Exercise 2: To-Do List App
Create functions to:
- Add tasks
- Remove completed tasks
- Find a specific task
- Show all tasks
Exercise 3: Shopping List
Create a shopping list where you can:
- Add items
- Remove items you don’t need
- Check if an item is already in the list
Key Takeaways
- Slices can grow: Use
append()to add items - Length vs Capacity: Length is items you have, capacity is space available
- Always assign append result:
slice = append(slice, item) - Watch for references: Some operations share memory
- Use make() for control:
make([]Type, length, capacity)
What’s Next?
Slices are incredibly powerful! They form the basis for most data structures in Go. Next, we’ll learn about Strings - how text works in programming!
Quick Quiz
- What’s the difference between an array and a slice?
- What does
append()return? - What’s the difference between length and capacity?
- How do you copy a slice safely?
- Why do we use
slice = append(slice, item)instead of justappend(slice, item)?
Remember: Slices are like expandable backpacks. Perfect when you don’t know how many items you’ll need!