Skip to main content

Overview

Slices are Go’s dynamic, flexible alternative to arrays. They provide a view into an underlying array and can grow or shrink as needed. Slices are the idiomatic way to work with collections in Go.
Slices are dynamic-size collections of elements. Unlike arrays, you don’t need to specify the size upfront.

Declaration and Initialization

There are several ways to create slices in Go:
slice/main.go
package main

import "fmt"

func main() {
	var nums = []int{1, 2, 3, 4, 5}
	//declaration of a slice we do not need to specify size
	//slices are dynamic size collection of elements
	// bydefault slices are nil

	println("Initial slice:", nums)

	var n = make([]int, 2, 5)
	//make function is used to create slices
	//first argument is type second is size
	//third is capacity

	n = append(n, 10)
	fmt.Println("Slice after append:", n)
    
	i:=0
	x:=[]int{}
	for i<10{
		x = append(x, i)
		i++
	}

	fmt.Println("Slice x:", x[5])
}

Creating Slices with make

The make function creates a slice with a specified length and optional capacity:
var n = make([]int, 2, 5)
// Type: []int
// Length: 2 (initial elements, all zero-initialized)
// Capacity: 5 (underlying array size)
Syntax: make([]Type, length, capacity)
  • Length: Number of elements in the slice (accessible immediately)
  • Capacity: Size of the underlying array (room for growth)

Length vs Capacity

PropertyDescriptionFunction
LengthNumber of elements currently in the slicelen(slice)
CapacityMaximum elements the underlying array can hold before reallocationcap(slice)

Nil Slices

By default, slices are nil until initialized:
var nums []int  // nums is nil
fmt.Println(nums == nil)  // true
fmt.Println(len(nums))    // 0
A nil slice has no underlying array. However, you can still safely call len() and cap() on it (both return 0).

Appending Elements

The append function adds elements to a slice and returns a new slice:
n = append(n, 10)
fmt.Println("Slice after append:", n)
// Output: Slice after append: [0 0 10]
append may allocate a new underlying array if the capacity is exceeded. Always assign the result back to the slice variable.

Dynamic Growth Example

i:=0
x:=[]int{}
for i<10{
    x = append(x, i)
    i++
}

fmt.Println("Slice x:", x[5])
// Output: Slice x: 5
This demonstrates building a slice dynamically - starting empty and growing to 10 elements.

Slice Operations

Accessing Elements

fmt.Println(x[5])  // Access element at index 5

Slicing (Sub-slices)

nums := []int{1, 2, 3, 4, 5}
sub := nums[1:4]  // [2, 3, 4] - indices 1 to 3 (4 is exclusive)
fmt.Println(sub)
Slicing syntax: slice[start:end]
  • Includes start index
  • Excludes end index
  • Omit start for beginning: [:3]
  • Omit end for end: [2:]

Iterating Over Slices

for i, value := range nums {
    fmt.Printf("Index: %d, Value: %d\n", i, value)
}

// Ignore index with underscore
for _, value := range nums {
    fmt.Println(value)
}

Memory Sharing

Slices are references to underlying arrays. Multiple slices can share the same array:
nums := []int{1, 2, 3, 4, 5}
sub := nums[1:3]  // [2, 3]
sub[0] = 99

fmt.Println(nums)  // [1, 99, 3, 4, 5] - original modified!
Modifying a sub-slice affects the original underlying array. Use copy() if you need independent slices.

Common Patterns

Creating a Copy

original := []int{1, 2, 3}
copy_slice := make([]int, len(original))
copy(copy_slice, original)

Removing an Element

nums := []int{1, 2, 3, 4, 5}
i := 2  // Remove index 2
nums = append(nums[:i], nums[i+1:]...)
// Result: [1, 2, 4, 5]

When to Use Slices

Use slices when:
  • You need a dynamically-sized collection
  • You don’t know the size at compile time
  • You want idiomatic Go code
  • You need to pass collections to functions efficiently
Slices are the default choice for most collection needs in Go. They combine the safety of arrays with the flexibility of dynamic resizing.

Next Steps

Now that you understand slices, explore maps for key-value pair collections.

Build docs developers (and LLMs) love