Slice Operations

Back

Loading concept...

Go Slice Operations: The Magic Backpack Adventure 🎒

Imagine you have a magic backpack. Unlike a regular backpack with fixed pockets, this one can grow, shrink, and even share its contents with other backpacks. That’s exactly what a slice is in Go!

Today, we’ll explore five superpowers of this magic backpack:

  1. Append – Adding more stuff
  2. Copy – Making duplicates
  3. Slice Expressions – Taking just what you need
  4. nil vs Empty – The difference between “no backpack” and “empty backpack”
  5. Reallocation – When the backpack upgrades itself

1. Slice Append: Growing Your Backpack

The Story

You’re packing for a trip. You start with 3 toys. Then your friend gives you 2 more. Your magic backpack just… grows!

How It Works

toys := []string{"car", "ball", "doll"}
toys = append(toys, "robot", "puzzle")
// toys is now: [car ball doll robot puzzle]

Key Points:

  • append() adds items to the end
  • Always reassign the result: toys = append(toys, ...)
  • Can add one or many items at once

Visual Flow

graph TD A["toys: car, ball, doll"] --> B["append robot, puzzle"] B --> C["toys: car, ball, doll, robot, puzzle"]

Appending One Slice to Another

Want to combine two backpacks?

bag1 := []int{1, 2, 3}
bag2 := []int{4, 5}
bag1 = append(bag1, bag2...)
// bag1 is now: [1 2 3 4 5]

The ... unpacks the second slice. Think of it like dumping everything from bag2 into bag1.


2. Slice Copy: Making a Twin Backpack

The Story

Your friend wants the same toys you have. You don’t want to share the same backpack (because if they lose a toy, you lose it too!). So you copy everything into their own backpack.

How It Works

original := []int{10, 20, 30}
twin := make([]int, len(original))
copied := copy(twin, original)
// twin is now: [10 20 30]
// copied = 3 (number of items copied)

Why Use Copy?

  • Creates an independent clone
  • Changes to twin won’t affect original

The Trap: Size Matters!

small := make([]int, 2)
big := []int{1, 2, 3, 4, 5}
n := copy(small, big)
// small = [1, 2]
// n = 2 (only 2 items fit!)

copy() only fills what fits. The destination must be big enough!

Visual Flow

graph TD A["original: 10, 20, 30"] --> B["copy to twin"] B --> C["twin: 10, 20, 30"] D["Changes to twin"] --> E["original stays same!"]

3. Slice Expressions: Taking a Piece

The Story

You have 10 toys, but you only want to play with toys 3 to 7. You reach into the backpack and pull out just that section!

Basic Slicing

nums := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
part := nums[3:7]
// part = [3 4 5 6]

The Rule: slice[start:end] gives items from index start up to (but NOT including) end.

Quick Reference

Expression Result Meaning
nums[2:5] [2 3 4] Index 2 to 4
nums[:4] [0 1 2 3] Start to index 3
nums[6:] [6 7 8 9] Index 6 to end
nums[:] full copy Everything!

The Warning: Shared Memory!

original := []int{1, 2, 3, 4, 5}
piece := original[1:4]  // [2 3 4]
piece[0] = 999
// original is now [1 999 3 4 5]!

Slices share the same underlying storage. Changing piece changes original too!

Full Slice Expression (Three-Index)

Want to limit the capacity?

data := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
limited := data[2:5:6]
// limited = [2 3 4]
// len = 3, cap = 4

Format: slice[start:end:maxCap]

This prevents the new slice from seeing beyond index 6.


4. nil and Empty Slices: No Backpack vs Empty Backpack

The Story

nil slice = You don’t even own a backpack yet. Empty slice = You have a backpack, but it’s empty.

Both have no toys, but they’re different!

How They Look

var nilSlice []int          // nil slice
emptySlice := []int{}       // empty slice
alsoEmpty := make([]int, 0) // also empty

The Comparison

Property nil Slice Empty Slice
len() 0 0
cap() 0 0
== nil true false
Safe to append? Yes! Yes!

Real Example

var items []int  // nil

items = append(items, 1)  // Works perfectly!
// items = [1]

Go is smart. Appending to nil automatically creates the slice for you.

When Does It Matter?

func process(data []int) {
    if data == nil {
        fmt.Println("No data provided")
        return
    }
    if len(data) == 0 {
        fmt.Println("Empty list")
        return
    }
    // Process data...
}
  • Check == nil when “nothing was given”
  • Check len() == 0 when “list exists but empty”

5. Slice Reallocation: The Auto-Upgrade

The Story

Your magic backpack has a capacity – how much it CAN hold. When you try to add more than it can hold, magic happens: the backpack transforms into a BIGGER one!

Understanding Capacity

pack := make([]int, 3, 5)
// Length: 3 (items inside)
// Capacity: 5 (total space)
graph TD A["pack"] --> B["len=3: items you have"] A --> C["cap=5: total space available"]

When Reallocation Happens

nums := make([]int, 2, 2)  // len=2, cap=2
nums = append(nums, 3)      // Need more space!
// Go creates NEW bigger slice
// Old: cap=2, New: cap=4 (doubled!)

The Rule: When len would exceed cap, Go:

  1. Creates a new, larger underlying array
  2. Copies all items
  3. Returns the new slice

Growth Pattern

Old Capacity New Capacity
0-256 doubles (Ă—2)
256+ grows by ~25%

Why Does This Matter?

// Inefficient: many reallocations
var data []int
for i := 0; i < 10000; i++ {
    data = append(data, i)
}

// Efficient: one allocation
data := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
    data = append(data, i)
}

If you know the size ahead, use make() with capacity!

Detecting Reallocation

a := []int{1, 2, 3}
b := a[:]
a = append(a, 4, 5, 6, 7, 8)

// Did reallocation happen?
// Change a[0] and check b[0]
a[0] = 999
fmt.Println(b[0])  // Still 1? Yes = reallocated!

After reallocation, a and b no longer share memory.


Quick Summary

Operation What It Does Remember
append() Adds items Always reassign!
copy() Duplicates Size of destination limits copy
slice[a:b] Takes portion Shares memory with original
nil slice No slice exists Safe to append
Empty slice Slice exists, 0 items != nil
Reallocation Auto-grows slice Happens when cap exceeded

The Big Picture

graph TD A["Create Slice"] --> B{Need more space?} B -->|Yes| C["append - grows automatically"] B -->|No| D["Just add items"] C --> E{Exceeds capacity?} E -->|Yes| F["REALLOCATION!&lt;br&gt;New bigger array"] E -->|No| G["Uses existing space"] F --> H["Old slices now independent"]

You’ve Got This!

Slices are one of Go’s most powerful features. Now you understand:

  • How to grow with append()
  • How to clone with copy()
  • How to slice with expressions
  • The difference between nil and empty
  • Why reallocation makes slices magical

Go build something awesome! 🚀

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.