Go Gopher How to Go

Slices are a key data type in Go, providing a more powerful interface to sequences than arrays. Unlike arrays, slices are dynamically-sized and flexible.

💡 Key Points

  • Slices are reference types that point to an underlying array
  • Modifying a slice modifies the underlying array
  • Multiple slices can share the same underlying array
  • append may allocate a new array if capacity is exceeded
  • Use make with capacity when size is known for better performance
  • Nil slices can be appended to and work with most operations
  • Slicing doesn't copy data, it creates a new slice header

Creating Slices

Slices can be created in multiple ways:

package main

import "fmt"

func main() {
    // Create a slice with make
    s := make([]string, 3)
    fmt.Println("empty:", s)

    // Set and get values
    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("set:", s)
    fmt.Println("get:", s[2])

    // Length of slice
    fmt.Println("len:", len(s))

    // Append to slice
    s = append(s, "d")
    s = append(s, "e", "f")
    fmt.Println("appended:", s)

    // Copy slice
    c := make([]string, len(s))
    copy(c, s)
    fmt.Println("copied:", c)
}
Output:
empty: [  ]
set: [a b c]
get: c
len: 3
appended: [a b c d e f]
copied: [a b c d e f]

Slice Operator

Slices support a "slice" operator with the syntax slice[low:high]:

package main

import "fmt"

func main() {
    s := []string{"a", "b", "c", "d", "e", "f"}
    
    // Get elements from index 2 to 5
    fmt.Println("sl1:", s[2:5])
    
    // Get elements up to index 5
    fmt.Println("sl2:", s[:5])
    
    // Get elements from index 2 onwards
    fmt.Println("sl3:", s[2:])
}
Output:
sl1: [c d e]
sl2: [a b c d e]
sl3: [c d e f]

Slice Literals

You can declare and initialize a slice in one line:

package main

import "fmt"

func main() {
    // Slice literal
    t := []string{"g", "h", "i"}
    fmt.Println("slice literal:", t)
    
    // Multi-dimensional slice
    twoD := make([][]int, 3)
    for i := 0; i < 3; i++ {
        innerLen := i + 1
        twoD[i] = make([]int, innerLen)
        for j := 0; j < innerLen; j++ {
            twoD[i][j] = i + j
        }
    }
    fmt.Println("2d: ", twoD)
}
Output:
slice literal: [g h i]
2d:  [[0] [1 2] [2 3 4]]

Slice Declaration Patterns

PatternExampleDescription
make([]T, length)make([]int, 5)Create slice with length, zero values
make([]T, length, capacity)make([]int, 5, 10)Create slice with length and capacity
[]T{values}[]int{1, 2, 3}Slice literal with values
var s []Tvar s []intDeclare nil slice
array[:]arr[:]Create slice from entire array
slice[low:high]s[1:3]Create slice from slice (subslice)

Slice Operations

OperationSyntaxExampleDescription
Lengthlen(s)len(s)Returns number of elements
Capacitycap(s)cap(s)Returns capacity of underlying array
Appendappend(s, elements...)append(s, 1, 2, 3)Add elements to end of slice
Copycopy(dst, src)copy(dst, src)Copy elements from src to dst
Slices[low:high]s[1:3]Create subslice from index low to high-1
Iteratefor i, v := range sfor i, v := range sLoop through index and value
Accesss[index]s[0]Get or set element at index

Slice Internals

ConceptDescriptionNotes
LengthNumber of elements in the sliceCan be less than or equal to capacity
CapacitySize of underlying array from first elementMaximum size before reallocation
PointerReference to first element of sliceSlices are reference types
Nil sliceZero value of a slicelen=0, cap=0, pointer=nil
Empty sliceSlice with length 0 but allocatedmake([]T, 0) or []T{}
GrowthCapacity doubles when append exceeds capacityAutomatic reallocation and copy

Common Patterns

PatternCodeUse Case
Pre-allocates := make([]T, 0, capacity)When you know approximate size
Append slices = append(s, other...)Concatenate two slices
Remove elements = append(s[:i], s[i+1:]...)Remove element at index i
Insert elements = append(s[:i], append([]T{x}, s[i:]...)...)Insert x at index i
Clear slices = s[:0]Keep capacity, reset length
Filter in places = s[:0]; for _, x := range original { if keep(x) { s = append(s, x) } }Efficient filtering