06 - Collections: Slices

📋 Jump to Takeaways

So far, every variable holds one value. One number, one string, one boolean. But what if you need a list of names? Or a collection of scores? You're not going to make 50 separate variables. You need a list. In Go, that list is called a slice.

Arrays (Briefly)

Go has arrays, but they have a fixed size:

var scores [3]int
scores[0] = 10
scores[1] = 20
scores[2] = 30
fmt.Println(scores) // [10 20 30]

The size is part of the type. A [3]int and a [4]int are different types. You can't add or remove elements. That's too rigid for most real work.

Slices

A slice is like an array that can grow and shrink. This is what you'll actually use:

scores := []int{10, 20, 30}
fmt.Println(scores) // [10 20 30]

No number inside the brackets. That's what makes it a slice instead of an array.

Here we defined []int — a slice of integers. But slices work with any type: []string, []float64, []bool, or even []MyType for custom types you define yourself. We'll cover those later when we get to structs.

You can also start with an empty slice and add to it:

var scores []int
fmt.Println(scores)      // []
fmt.Println(len(scores)) // 0

append

append adds elements to a slice and returns a new slice:

names := []string{}
names = append(names, "Alice")
names = append(names, "Bob")
fmt.Println(names) // [Alice Bob]

You must reassign the result: names = append(names, ...). append doesn't modify the original, it returns a new slice with the element added.

You can append multiple elements at once:

names = append(names, "Carol", "Dave")

Accessing Elements

Elements are accessed by index, starting at 0:

names := []string{"Alice", "Bob", "Carol"}
fmt.Println(names[0]) // Alice
fmt.Println(names[1]) // Bob
fmt.Println(names[2]) // Carol

Accessing an index that doesn't exist crashes the program:

fmt.Println(names[5]) // panic: index out of range

Always check len() before accessing by index if you're not sure.

len

len returns the number of elements:

names := []string{"Alice", "Bob"}
fmt.Println(len(names)) // 2

Looping Over Slices

range gives you the index and value for each element:

names := []string{"Alice", "Bob", "Carol"}

for i, name := range names {
	fmt.Printf("%d: %s\n", i, name)
}
// 0: Alice
// 1: Bob
// 2: Carol

If you only need the value:

for _, name := range names {
	fmt.Println(name)
}

Slicing a Slice

You can grab a portion of a slice with [start:end]:

nums := []int{10, 20, 30, 40, 50}
fmt.Println(nums[1:3]) // [20 30]
fmt.Println(nums[:2])  // [10 20]
fmt.Println(nums[3:])  // [40 50]

start is inclusive, end is exclusive.

If start or end is beyond the slice's length, Go panics at runtime:

nums := []int{10, 20, 30, 40, 50}
fmt.Println(nums[:6]) // panic: slice bounds out of range

Removing an Element

Go doesn't have a built-in "remove" function. You combine slicing and append:

names := []string{"Alice", "Bob", "Carol"}

// Remove index 1 ("Bob")
names = append(names[:1], names[2:]...)
fmt.Println(names) // [Alice Carol]

The ... unpacks the second slice so append can add each element individually.

Key Takeaways

  • Arrays have a fixed size. Slices can grow and shrink. Use slices
  • []string{} creates an empty slice. []string{"a", "b"} creates one with values
  • append(slice, element) returns a new slice with the element added. Always reassign
  • len(slice) returns the number of elements
  • slice[index] accesses an element. Indexes start at 0
  • range loops over a slice giving index and value
  • slice[start:end] creates a sub-slice. Start is inclusive, end is exclusive
  • Remove elements by combining append with slicing: append(s[:i], s[i+1:]...)

🚀 Ready to run?

Complete runnable examples for this lesson.

📝 Ready to test your knowledge?

Answer the quiz below to mark this lesson complete.

© 2026 ByteLearn.dev. Free courses for developers. · Privacy