Understanding the Relationship Between Slices and Arrays in Go

Reza
3 min readNov 15, 2024

--

Slices in Go are closely tied to arrays. Under the hood, a slice is implemented with a reference to an underlying array. The capacity of a slice corresponds to the length of this array, and the slice essentially acts as a view over the array. Because of this relationship, modifying a slice can directly impact the underlying array.

However, when the slice’s capacity changes — usually due to operations like append — a new array is allocated, and the connection to the original array is severed. Let’s explore this with an example.

Here’s the code that demonstrates the relationship between slices and arrays:

package main

import (
"fmt"
)

func modify(slice []string) {
slice[0] = "Modified_in_function"
}

The modify function changes the first element of the input slice.

Working with Arrays and Slices

In the main function, we’ll define an array and experiment with slices that reference parts of this array.

func main() {
arr := [4]string{"Alpha", "Beta", "Gamma", "Delta"}
fmt.Println("arr:", arr)

Here, we define an array named arr with four elements.

 var sliceA = arr[0:1]
fmt.Println("sliceA:", sliceA)
sliceA[0] = "sliceA"

In this snippet, sliceA is connected to the first element of arr. After modifying sliceA, the corresponding element in arr is updated as well.

 var sliceB = arr[1:3]
fmt.Println("sliceB:", sliceB)
sliceB[0] = "sliceB_0"
sliceB[1] = "sliceB_1"

Here, sliceB references arr[1] and arr[2]. When the values in sliceB are updated, the changes are reflected in the array.

 fmt.Println("arr after modifications:", arr)

Even though arr wasn’t directly modified, its contents changed due to the updates in sliceA and sliceB.

Slice Modifications Inside Functions

 modify(sliceB)
fmt.Println("arr after function call:", arr)

Passing sliceB to the modify function demonstrates that changes made to a slice — even within a function — will affect the underlying array.

Capacity and Reconnection

 fmt.Println("Capacity of sliceA:", cap(sliceA), "Length of sliceA:", len(sliceA))
sliceA = append(sliceA, "Extra1", "Extra2", "Extra3")
fmt.Println("sliceA after appends:", sliceA)
arr[0] = "-Modified-"

When the capacity of sliceA increases due to appending elements, a new array is allocated, and sliceA is no longer connected to arr.

 sliceA = append(sliceA, "Extra4")
fmt.Println("Capacity of sliceA:", cap(sliceA), "Length of sliceA:", len(sliceA))
arr[0] = "-AnotherModification-"
arr[1] = "-StillConnected-"

In this example, arr and sliceB remain connected because the capacity of sliceB hasn’t changed, but sliceA is now independent.

Final Output

 fmt.Println("sliceA:", sliceA)
fmt.Println("arr:", arr)
fmt.Println("sliceB:", sliceB)
}

Output of the Code

When you run the program, you’ll see the following output, illustrating how slices and arrays interact:

arr: [Alpha Beta Gamma Delta]
sliceA: [Alpha]
sliceB: [Beta Gamma]
arr after modifications: [sliceA sliceB_0 sliceB_1 Delta]
arr after function call: [sliceA Modified_in_function sliceB_1 Delta]
Capacity of sliceA: 4 Length of sliceA: 1
sliceA after appends: [sliceA Extra1 Extra2 Extra3]
Capacity of sliceA: 8 Length of sliceA: 5
sliceA: [-Modified- Extra1 Extra2 Extra3 Extra4]
arr: [-AnotherModification- -StillConnected- sliceB_1 Delta]
sliceB: [-StillConnected- sliceB_1]

Key Takeaways

1. Slices in Go reference an underlying array. Modifications to the slice affect the array.

2. If a slice’s capacity changes (e.g., via append), the connection to the original array is severed.

3. Understanding the connection between slices and arrays is crucial for avoiding unexpected side effects in Go programs.

This behavior makes slices both powerful and flexible, but developers must manage them carefully to ensure code correctness.

--

--

Reza
Reza

Written by Reza

CloudNative IT Infrastracture Engineer

No responses yet