100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Go

Slices in Go

A dynamically-sized, flexible view into an underlying array, the go-to collection type in idiomatic Go.

Data StructuresBeginner9 min readJul 8, 2026
Analogies

Introduction

A slice is Go's primary abstraction for working with sequences of data. Unlike an array, a slice does not have a fixed size baked into its type; instead, it describes a window into an underlying array using a pointer, a length, and a capacity. Slices can grow using the built-in append function, which may allocate a new, larger backing array when the current capacity is exceeded. This combination of flexibility and efficiency is why slices, not arrays, are used throughout everyday Go code.

🏏

Cricket analogy: A fixed-size array is like a stadium with a set number of seats, while a slice is like a scoreboard view into that stadium showing only certain rows; when more fans arrive than the view allows, append moves the view to a bigger stadium entirely.

Syntax

go
// Slice literal
nums := []int{1, 2, 3}

// make(type, length, capacity)
buf := make([]int, 0, 10)

// Slicing an existing array or slice: s[low:high]
arr := [5]int{10, 20, 30, 40, 50}
sub := arr[1:3] // [20 30]

// Appending
buf = append(buf, 1, 2, 3)

Explanation

make([]int, 0, 10) creates a slice with length 0 and capacity 10, preallocating room to grow without reallocating on every append. The slicing expression s[low:high] selects elements from index low up to but not including high, and the result shares the same underlying array as the original, meaning mutations through one slice can be visible through the other. append adds elements to the end; if the slice's capacity is exhausted, Go allocates a new, larger array (commonly doubling capacity for smaller slices) and copies the existing elements over, after which the slice header points to the new array. This sharing behavior is a common source of bugs when two slices unexpectedly alias the same memory.

🏏

Cricket analogy: make([]int, 0, 10) is like reserving 10 seats in the stands before fans arrive, so no reshuffling is needed as people fill in; s[low:high] is like selecting overs 3 through 7 from the same scorecard, and append that exceeds capacity is like moving the whole crowd to a bigger stand and copying the seating chart.

Example

go
package main

import "fmt"

func main() {
	original := []int{1, 2, 3, 4, 5}
	window := original[1:3]

	fmt.Println("window:", window, "len:", len(window), "cap:", cap(window))

	// Mutating the window affects the original backing array
	window[0] = 99
	fmt.Println("original after mutation:", original)

	// append may or may not reallocate depending on capacity
	grown := append(window, 100)
	fmt.Println("grown:", grown)
	fmt.Println("original after append:", original)
}

Output

go
window: [2 3] len: 2 cap: 4
original after mutation: [1 99 3 4 5]
grown: [99 3 100]
original after append: [1 99 3 100 5]

Key Takeaways

  • A slice header holds a pointer to an array, a length, and a capacity.
  • Slicing shares the underlying array, so mutations can be visible across slices.
  • append grows a slice, reallocating a new backing array once capacity is exceeded.
  • make(type, len, cap) lets you preallocate capacity to reduce reallocations.
  • Because slices can silently alias memory, copy() is used to make independent copies when needed.

Practice what you learned

Was this page helpful?

Topics covered

#Go#GoProgrammingStudyNotes#Programming#SlicesInGo#Slices#Syntax#Explanation#Example#StudyNotes#SkillVeris