Understanding Go Arrays and Slices: Key Differences and Usage Patterns

Arrays in Go

An array in Go is a fixed-size collection of elements of the same type. Unlike other programming languages, Go arrays have a fixed length that cannot be modified after declaration.

Array Declaration and Basic Operations

Arrays are declared using the following syntax:

var variable_name [size]data_type
var numbers [5]int

numbers[0] = 10
numbers[1] = 20
numbers[2] = 30
numbers[3] = 40
numbers[4] = 50

fmt.Printf("Type: %T\n", numbers)
fmt.Println("Element at index 2:", numbers[2])
fmt.Println("Length:", len(numbers))
fmt.Println("Capacity:", cap(numbers))

Array Initialization Methods

Go provides several ways to initialize arrays:

// Method 1: Explicit declaration with values
var data1 = [6]int{10, 20, 30, 40, 50, 60}

// Method 2: Short declaration
data2 := [4]string{"apple", "banana", "cherry", "date"}

// Method 3: Compiler counts elements automatically
data3 := [...]int{1, 2, 3}

// Method 4: Sparse initialization with specific indices
data4 := [6]int{0: 99, 3: 88}
// Result: [99 0 0 88 0 0]

Iterating Through Arrays

Using a traditional for loop:

scores := [5]int{85, 90, 78, 92, 88}
for i := 0; i < len(scores); i++ {
    fmt.Printf("Index %d: %d\n", i, scores[i])
}

Using the range-based loop:

for idx, val := range scores {
    fmt.Printf("Position %d holds value %d\n", idx, val)
}

Value Semantics

In Go, arrays are value types. When you assign one array to another, the entire contents are copied:

original := [3]int{1, 2, 3}
duplicate := original
duplicate[0] = 100

fmt.Println("Original:", original)  // [1 2 3]
fmt.Println("Duplicate:", duplicate) // [100 2 3]

Multi-Dimensional Arrays

Go supports arrays with multiple dimensions:

matrix := [3][3]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

for _, row := range matrix {
    fmt.Println(row)
}

Slices in Go

A slice is a dynamic, flexible view into an underlying array. Unlike arrays, slices can grow and shrink as needed.

Slice Declaration

// Empty slice
emptySlice := []int{}

// Slice with initial values
initialized := []int{10, 20, 30}
fmt.Printf("Type: %T, Value: %v\n", initialized, initialized)

Understanding Length vs Capacity

  • Length: The number of elements currently stored in the slice
  • Capacity: The total space available in the underlying array before reallocation is needed

Creating Slices with make

The make function allocates and initializes a slice with specified length and capacity:

make([]element_type, length, capacity)
buffer := make([]int, 3, 8)
fmt.Printf("Length: %d, Capacity: %d\n", len(buffer), cap(buffer))
fmt.Println("Initial values:", buffer)

buffer[0] = 42
buffer[1] = 73
buffer[2] = 99
fmt.Println("After assignment:", buffer)

Growing Slices with append

When elements exceed capacity, Go doubles the capacity (for larger slices, the growth factor is smaller):

dynamic := make([]int, 2, 4)
fmt.Printf("Before append - Length: %d, Capacity: %d\n", len(dynamic), cap(dynamic))

dynamic = append(dynamic, 1, 2, 3, 4, 5)
fmt.Printf("After append - Length: %d, Capacity: %d\n", len(dynamic), cap(dynamic))
fmt.Println("Contents:", dynamic)

Appending one slice to another requires the spread operator:

a := []int{1, 2}
b := []int{3, 4}
a = append(a, b...)
fmt.Println("Combined:", a)

Creating Slices from Arrays

Slices can be created as views into existing arrays:

fullArray := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

segment1 := fullArray[0:5]   // Elements 0-4
segment2 := fullArray[3:7]   // Elements 3-6
segment3 := fullArray[5:]    // Elements 5-9
segment4 := fullArray[:]     // All elements

fmt.Println(segment1) // [0 1 2 3 4]
fmt.Println(segment2) // [3 4 5 6]

Because slices reference the same underlying array, modifications affect both:

numbers := [5]int{10, 20, 30, 40, 50}
view := numbers[1:4]

numbers[2] = 99
fmt.Println("Array:", numbers)     // [10 20 99 40 50]
fmt.Println("Slice:", view)        // [20 99 40]

view[0] = 88
fmt.Println("Array:", numbers)     // [10 88 99 40 50]
fmt.Println("Slice:", view)        // [88 99 40]

Reference Semantics

Slices are reference types. Assigning one slice to another makes both point to the same underlying array:

alpha := []int{1, 2, 3}
beta := alpha
beta[0] = 100

fmt.Println("Alpha:", alpha) // [100 2 3]
fmt.Println("Beta:", beta)   // [100 2 3]

Deep Copy with copy

To create an independent copy of a slice, use the built-in copy functon:

source := []int{1, 2, 3, 4, 5}
destination := make([]int, len(source))
copy(destination, source)

destination[0] = 99
fmt.Println("Source:", source)      // [1 2 3 4 5]
fmt.Println("Destination:", destination) // [99 2 3 4 5]

Manual deep copy using append:

source := []int{10, 20, 30}
clone := make([]int, 0)
for _, v := range source {
    clone = append(clone, v)
}
clone[1] = 200
fmt.Println("Source:", source) // [10 20 30]
fmt.Println("Clone:", clone)   // [10 200 30]

Tags: Go Arrays slices programming

Posted on Thu, 30 Jul 2026 16:53:57 +0000 by pomme