Sobes.tech
Junior

How do slices work in Go?

sobes.tech AI

Answer from AI

A slice is a lightweight wrapper over an array. It does not store data itself but references a base array.

Slice structure:

  • Pointer to the base array: The address of the first element of the slice in memory.
  • Length: The number of elements in the slice.
  • Capacity: The number of elements in the underlying array available from the slice pointer.
// Example of declaring a slice
var s []int
s = append(s, 1, 2, 3) // Adding elements

// Internal representation of slice s (simplified)
// struct SliceHeader {
//     Data uintptr // Pointer to the base array
//     Len int      // Length of the slice
//     Cap int      // Capacity of the slice
// }
  • When creating a slice with make([]T, length, capacity), a base array of the specified capacity is created, and the slice references it with the specified length.
  • When slicing an array or another slice (e.g., arr[low:high:max]), a new slice is created that references the same memory area of the base array but with different pointer, length, and capacity.
  • The append operation may lead to reallocation. If the current capacity is insufficient to add new elements, Go creates a new, larger base array, copies the old array's elements into it, and updates the slice pointer to the new array. This is called reallocation. The growth algorithm for capacity during append is exponential (doubling up to a certain size, then slowing growth).
  • Slices are passed by value, but since they contain a pointer to the base array, changes to slice elements (via index) will be visible in all slices referencing the same base array.
  • Changing the length of a slice via slicing does not affect the base array but may limit access to parts of the base array data.

Table: Comparison of Length and Capacity

Characteristic Length Capacity
Definition Actual number of elements in the slice Maximum number of elements the slice can hold without reallocation
Access Used to access elements (s[i]) Determines how many more elements can be added via append before reallocation
Retrieval len(s) cap(s)
// Example of working with capacity and length
s := make([]int, 0, 5) // Length 0, capacity 5
fmt.Println(len(s), cap(s)) // 0 5

s = append(s, 1, 2) // Adding 2 elements
fmt.Println(len(s), cap(s)) // 2 5

s = append(s, 3, 4, 5, 6) // Adding 4 more elements, reallocation needed
fmt.Println(len(s), cap(s)) // 6 10 (capacity increased)