Understanding Slices and Sub-slices in Go

Core Characteristics

In Go, slices are reference types composed of three components: a pointer to the underlying array, length, and capacity. This combination forms the slice header.

Key features include:

  • Dynamic size - length and capacity can differ
  • Reference semantics - copies share the same underlying data
  • Built on arrays for efficient indexed access

Declaration Methods

Literal Initialization

var numbers = []int{10, 20, 30}
fmt.Printf("%T %v", numbers, numbers) // Output: []int [10 20 30]

Empty Slice (Not Recommended)

var empty []string
fmt.Println(len(empty), cap(empty)) // Output: 0 0

Using make (Recommended)

data := make([]float64, 3, 5)
fmt.Println(data, len(data), cap(data)) // Output: [0 0 0] 3 5

Memory Model

The slice header contains:

  • Pointer to array elements
  • Current length (accessible elements)
  • Total capacity (maximum elements before relalocation)
values := []int{1, 2, 3}
fmt.Printf("%p %p", &values, &values[0]) // Different addresses

Appending Elements

The append function returns a new header, potentially triggering reallocation:

nums := make([]int, 2, 4)
nums = append(nums, 5)
fmt.Println(nums, len(nums), cap(nums)) // Output: [0 0 5] 3 4

Sub-slice Operations

Basic Syntax

Create views of existing slices with [start:end] notation:

mainSlice := []int{10, 20, 30, 40, 50}
subSlice := mainSlice[1:4] // [20 30 40]
fmt.Println(len(subSlice), cap(subSlice)) // Output: 3 4

Memory Sharing

Sub-slices share the same underlying array:

original := []int{1, 2, 3, 4}
view := original[1:3]
view[0] = 99
fmt.Println(original) // Output: [1 99 3 4]

Capacity Calculation

Sub-slice capacity extends from start index to end of array:

full := []int{0, 1, 2, 3, 4}
partial := full[2:4] // capacity = 3 (indices 2-4)
fmt.Println(len(partial), cap(partial)) // Output: 2 3

Practical Considerations

Growth Strategy

Go's slice growth algorithm:

  • Below 256 elements: double capacity
  • Above 256: 1.25x growth plus fixed buffer

Performence Tips

  • Pre-allocate with make when size is known
  • Avoid frequent small appends
  • Remember sub-slice modifications affect parent

Common Operations

Removing Duplicates

func deduplicate(s []int) []int {
    seen := make(map[int]bool)
    result := []int{}
    for _, v := range s {
        if !seen[v] {
            seen[v] = true
            result = append(result, v)
        }
    }
    return result
}

Reversing Elements

func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

Tags: Golang slices memory-management data-structures Arrays

Posted on Sat, 24 Oct 2026 16:35:35 +0000 by ivory_kitten