Skip to main content
Go: Arrays and Slices
  1. Posts/

Go: Arrays and Slices

·541 words·3 mins·
Roman
Author
Roman
Photographer with MSci in Computer Science and a Home Lab obsession
Table of Contents

Following the Learn Go with Tests guide, exploring Go’s collection types: arrays and slices.

Arrays vs Slices
#

Arrays: Fixed size, encoded in the type

numbers := [5]int{1, 2, 3, 4, 5}        // fixed size array
numbers := [...]int{1, 2, 3, 4, 5}      // compiler counts elements

Slices: Dynamic size, more flexible

numbers := []int{1, 2, 3, 4, 5}         // slice (no size specified)

[5]int and [4]int are different types - you can’t pass a 4-element array to a function expecting a 5-element array.

Building a Sum Function
#

Starting with arrays, then refactoring to slices:

func Sum(numbers []int) int {
    sum := 0
    for _, number := range numbers {
        sum += number
    }
    return sum
}

Range Keyword:

  • With index: for i, number := range numbers
  • Index ignored: for _, number := range numbers

Comparing Slices
#

Go doesn’t allow == comparison for slices. Use slices.Equal():

import "slices"

func TestSum(t *testing.T) {
    got := []int{3, 9}
    want := []int{3, 9}
    
    if !slices.Equal(got, want) {
        t.Errorf("got %v want %v", got, want)
    }
}

Creating Slices
#

Using make()
#

// Create slice with specific length and capacity
sums := make([]int, 5)        // length 5, capacity 5
sums := make([]int, 0, 10)    // length 0, capacity 10

Using append()
#

var sums []int                // zero-value slice (nil)
sums = append(sums, 42)       // append returns new slice

Important: append() returns a new slice - always assign the result back.

Variadic Functions
#

Accept variable number of arguments:

func SumAll(numbersToSum ...[]int) []int {
    var sums []int
    for _, numbers := range numbersToSum {
        sums = append(sums, Sum(numbers))
    }
    return sums
}

// Usage
result := SumAll([]int{1, 2}, []int{3, 4}, []int{5})

Slice Operations
#

Slicing Slices
#

numbers := []int{1, 2, 3, 4, 5}
tail := numbers[1:]           // [2, 3, 4, 5] - from index 1 to end
head := numbers[:2]           // [1, 2] - from start to index 2
middle := numbers[1:4]        // [2, 3, 4] - from index 1 to 4

Safe Slicing
#

Always check bounds to avoid runtime panics - use len() to verify slice length before slicing.

Test Helper Functions
#

Extract common test logic for cleaner tests:

func TestSumAllTails(t *testing.T) {
    checkSums := func(t testing.TB, got, want []int) {
        t.Helper()
        if !slices.Equal(got, want) {
            t.Errorf("got %v want %v", got, want)
        }
    }
    
    t.Run("normal slices", func(t *testing.T) {
        got := SumAllTails([]int{1, 2}, []int{0, 9})
        want := []int{2, 9}
        checkSums(t, got, want)
    })
}

Test Coverage
#

Go includes built-in coverage analysis:

go test -cover

Example output:

PASS
coverage: 100.0% of statements

Memory Management Examples
#

Slice References vs Copies
#

x := [3]string{"Лайка", "Белка", "Стрелка"}

y := x[:]                    // slice points to array x
z := make([]string, len(x))
copy(z, x[:])               // z is independent copy

y[1] = "Belka"              // changes both x and y
// x: [3]string [Лайка Belka Стрелка]
// y: []string [Лайка Belka Стрелка]  
// z: []string [Лайка Белка Стрелка]  (unchanged)

Key Point: Slices share underlying arrays unless explicitly copied.

Memory Leak Prevention
#

a := make([]int, 1e6)       // 1 million element slice
b := a[:2]                  // small slice, but keeps entire array in memory

c := make([]int, len(b))    // create independent copy
copy(c, b)                  // now 'a' can be garbage collected

Learn More: Go Slices Blog