Go Slices & Arrays

Arrays vs Slices

Arrays are fixed-size value types
var a [5]int              // 5 zeros
b := [3]string{"x", "y", "z"}
c := [...]int{1, 2, 3}   // size inferred from elements

// Arrays are values — assignment copies
d := b
d[0] = "changed"
fmt.Println(b[0])  // "x" — b is unchanged

Arrays have a fixed size that is part of the type: [3]int and [5]int are different types. You almost never use raw arrays — slices are the idiomatic choice.

Creating Slices

Slice literal, make, and nil slices
// Literal
s := []int{10, 20, 30}

// make(type, length, capacity)
buf := make([]byte, 0, 1024)  // len=0, cap=1024

// nil slice — valid, zero length
var empty []int
fmt.Println(len(empty))   // 0
fmt.Println(empty == nil)  // true

make pre-allocates capacity without filling it. A nil slice is functionally identical to an empty slice for most operations — len, cap, range, and append all work correctly on nil slices.

Append & Growth

append returns a new slice header
s := []int{1, 2, 3}
s = append(s, 4)          // always reassign
s = append(s, 5, 6, 7)    // variadic

// Append one slice to another
more := []int{8, 9}
s = append(s, more...)     // unpack with ...

append may allocate a new backing array if capacity is exceeded. Always capture the return value: s = append(s, …​). Forgetting the assignment is a common bug.

Slicing Operations

Slice expressions create views into the same array
data := []int{0, 1, 2, 3, 4, 5}

sub  := data[1:4]    // [1, 2, 3] — indices 1, 2, 3
head := data[:3]     // [0, 1, 2]
tail := data[3:]     // [3, 4, 5]
all  := data[:]      // full slice — same backing array

// Full slice expression — limits capacity
safe := data[1:4:4]  // len=3, cap=3 — append won't overwrite data[4]

Slices share the underlying array. Modifying sub[0] changes data[1]. The three-index slice [low:high:max] controls capacity, preventing append from overwriting adjacent elements.

Common Patterns

Remove element at index (order-preserving)
func remove(s []int, i int) []int {
    return append(s[:i], s[i+1:]...)
}
Filter a slice in place
func filter(s []string, pred func(string) bool) []string {
    result := s[:0]  // reuse backing array, zero length
    for _, v := range s {
        if pred(v) {
            result = append(result, v)
        }
    }
    return result
}

s[:0] creates a zero-length slice backed by the same array — avoids allocation when the result is smaller than the input.