The Difference between Array and Slice in Go

Discover how arrays and slices vary fundamentally to help you navigate the subtleties of Go programming.Enhance your coding expertise with this insightful guide
E
Edtoks4:48 min read

In Go, arrays and slices are both used to store collections of elements, but they have key differences in terms of size, flexibility, and usage.

Arrays:

  1. Fixed Size:

    • Arrays have a fixed size that is determined at the time of declaration. Once defined, the size cannot be changed.

  2. Value Type:

    • Arrays are value types. When you assign an array to another variable or pass it to a function, a copy of the array is made.

  3. Declaration:

    • Here's an example of declaring an array:

      var myArray [3]int
  4. Initialization:

    • Arrays can be initialized at the time of declaration:

      myArray := [3]int{1, 2, 3}

Slices:

  1. Dynamic Size:

    • Slices, on the other hand, are dynamic and can grow or shrink in size. The size of a slice is not fixed.

  2. Reference Type:

    • Slices are reference types. When you assign a slice to another variable or pass it to a function, you are working with a reference to the underlying array.

  3. Declaration:

    • Slices are declared without specifying a size:

      var mySlice []int
  4. Initialization:

    • Slices can be initialized using the make function or by slicing an existing array or another slice:

      mySlice := make([]int, 3) // Using make
      anotherSlice := myArray[:2] // Slicing an array

Examples:

Let's delve into the internals with examples:

Arrays:

package main

import "fmt"

func main() {
    // Declaration and initialization of an array
    var myArray [3]int
    myArray[0] = 1
    myArray[1] = 2
    myArray[2] = 3

    // Printing the array
    fmt.Println("Array:", myArray)
}

Slices:

package main

import "fmt"

func main() {
    // Declaration and initialization of a slice
    mySlice := []int{1, 2, 3}

    // Appending elements to the slice
    mySlice = append(mySlice, 4, 5)

    // Printing the slice
    fmt.Println("Slice:", mySlice)

    // Slicing an existing array
    myArray := [5]int{1, 2, 3, 4, 5}
    slicedArray := myArray[1:4]
    fmt.Println("Sliced Array:", slicedArray)
}

Key Differences:

  1. Dynamic Size:

    • Arrays have a fixed size, while slices can dynamically grow or shrink.

  2. Value vs. Reference:

    • Arrays are value types; modifications don't affect the original array. Slices are reference types; modifications affect the original slice.

  3. Flexibility:

    • Slices are more flexible for dynamic scenarios, while arrays are useful for a fixed set of elements.

Let's explore the internals of how arrays and slices are stored and how slices dynamically increase their size.

Internals of Arrays:

Arrays in Go are contiguous blocks of memory where elements are stored. The size of an array is fixed and determined at compile time. Each element in the array occupies a specific memory location, and the array itself is a fixed-size data structure.

Here's an illustration of how an array is stored in memory:

+---+---+---+
| 1 | 2 | 3 |
+---+---+---+

In this example, an array [3]int is represented with three integer elements.

Internals of Slices:

Slices in Go are more dynamic and flexible than arrays. Internally, a slice is a data structure that consists of a pointer to the underlying array, a length, and a capacity. The pointer points to the first element of the slice, the length represents the number of elements in the slice, and the capacity is the maximum number of elements that the slice can hold without reallocation.

Here's an illustration of the internals of a slice:

Slice: [1 2 3]
+---+---+---+---+---+
| 1 | 2 | 3 |   |   |
+---+---+---+---+---+
^               ^   ^
|               |   |
Pointer         Length
                Capacity

In this example, the slice has a pointer pointing to the first element of the underlying array (1), a length of 3 indicating the number of elements (1, 2, 3), and a capacity of 5 indicating the maximum number of elements the slice can hold without reallocation.

Dynamic Size Increase in Slices:

When you append elements to a slice and the capacity is exceeded, Go automatically creates a new underlying array with a larger capacity, copies the existing elements, and adds the new elements. The pointer and length of the slice are then updated to reflect the changes.

Here's an illustration of dynamically increasing the size of a slice:

// Original Slice: [1 2 3]
// New Slice after append: [1 2 3 4 5]

Internally, the process involves creating a new underlying array, copying the elements, and updating the slice's pointer, length, and capacity.

Understanding these internal mechanisms helps in realizing the efficiency and convenience provided by slices in Go, especially when dealing with dynamic collections of data.

Understanding these differences helps in choosing the right data structure for a specific use case. Arrays are handy when the size is fixed, and slices are versatile for dynamic situations.

Let's keep in touch!

Subscribe to keep up with latest updates. We promise not to spam you.