Go Slices

A slice is a flexible, dynamic view into an array. Unlike arrays, slices can grow and shrink. Slices are the most commonly used data structure in Go for working with lists of elements.

Creating a Slice

Slice Literal

package main

import "fmt"

func main() {
    fruits := []string{"Apple", "Banana", "Mango"}
    fmt.Println(fruits)    // [Apple Banana Mango]
    fmt.Println(len(fruits)) // 3
}

Using make

package main

import "fmt"

func main() {
    scores := make([]int, 5) // creates a slice of 5 zeros
    fmt.Println(scores)      // [0 0 0 0 0]
}

Slice Internal Structure

A slice has three parts:

┌───────────────────────────────────┐
│  pointer  │  length  │  capacity  │
│  to array │   (len)  │    (cap)   │
└───────────────────────────────────┘

pointer  → points to the underlying array
length   → number of elements currently in the slice
capacity → total space available before reallocation

Appending to a Slice

Use append to add elements. If the slice runs out of capacity, Go automatically allocates a larger underlying array.

package main

import "fmt"

func main() {
    nums := []int{1, 2, 3}
    nums = append(nums, 4)
    nums = append(nums, 5, 6, 7)

    fmt.Println(nums) // [1 2 3 4 5 6 7]
}

Slicing a Slice

A sub-slice is created using the [low:high] syntax. The result includes elements from index low up to but not including index high.

package main

import "fmt"

func main() {
    letters := []string{"A", "B", "C", "D", "E"}

    fmt.Println(letters[1:4]) // [B C D]
    fmt.Println(letters[:3])  // [A B C]
    fmt.Println(letters[2:])  // [C D E]
}

Slicing Diagram

letters := ["A", "B", "C", "D", "E"]
Index:        0     1     2     3     4

letters[1:4]  →  elements at index 1, 2, 3  →  [B C D]
                 (includes 1, excludes 4)

Slices Share the Underlying Array

A sub-slice points to the same memory as the original. Modifying the sub-slice changes the original slice too.

package main

import "fmt"

func main() {
    original := []int{10, 20, 30, 40, 50}
    sub := original[1:4]

    sub[0] = 999

    fmt.Println(original) // [10 999 30 40 50]  ← changed!
    fmt.Println(sub)      // [999 30 40]
}

To avoid this shared-memory issue, use copy to create an independent slice.

Copying a Slice

package main

import "fmt"

func main() {
    src := []int{1, 2, 3, 4, 5}
    dst := make([]int, len(src))
    copy(dst, src)

    dst[0] = 100
    fmt.Println(src) // [1 2 3 4 5] — unchanged
    fmt.Println(dst) // [100 2 3 4 5]
}

Deleting an Element from a Slice

Go has no built-in delete for slices. The standard pattern appends the elements before and after the target index.

package main

import "fmt"

func main() {
    items := []string{"A", "B", "C", "D"}
    i := 2 // delete index 2 ("C")

    items = append(items[:i], items[i+1:]...)
    fmt.Println(items) // [A B D]
}

2D Slice

package main

import "fmt"

func main() {
    matrix := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }

    for _, row := range matrix {
        fmt.Println(row)
    }
}

Output:

[1 2 3]
[4 5 6]
[7 8 9]

Key Slice Functions

OperationCode
Lengthlen(s)
Capacitycap(s)
Append elements = append(s, val)
Append slices = append(s, other...)
Copycopy(dst, src)
Sub-slices[low:high]

Key Points

  • Slices are dynamic — they grow automatically with append
  • A slice has a pointer, length, and capacity
  • Sub-slices share memory with the original — use copy for independence
  • The [low:high] syntax includes low and excludes high
  • Slices are preferred over arrays in most Go programs

Leave a Comment