GGistDev

Arrays in Go

Arrays are fixed-size, value-type collections. They’re less common than slices in everyday Go, but worth knowing for correctness, performance, and interop with low-level operations.

Key ideas

  • Fixed length is part of the type: [3]int[5]int
  • Value semantics: assignment and function arguments copy the whole array
  • Backing store for slices: slice := arr[:]
  • Comparable when element types are comparable Detailed:
  • Arrays live on the stack when small; large arrays may be heap-allocated depending on escape analysis
  • Using arrays as map keys is allowed if elements are comparable

Declaring and initializing

Declare zero-initialized arrays, use literals with explicit or inferred length, and index keys for sparse initialization.

package main
import "fmt"

func main() {
    var a [3]int                  // zero-initialized
    b := [3]int{1, 2, 3}         // literal
    c := [...]string{"go", "py"} // length inferred
    d := [5]int{0: 10, 3: 99}    // index keys

    fmt.Println(a, b, c, d)
}

Access, update, iterate

Indexing is bounds-checked at runtime; out-of-range panics. Iterate with classic for or range.

package main
import "fmt"

func main() {
    scores := [5]int{85, 92, 78, 96, 88}

    // access/update
    fmt.Println(scores[0], scores[len(scores)-1])
    scores[1] = 95

    // for with index
    for i := 0; i < len(scores); i++ {
        fmt.Printf("%d ", scores[i])
    }
    fmt.Println()

    // range (index, value)
    sum := 0
    for i, v := range scores {
        fmt.Printf("i=%d v=%d\n", i, v)
        sum += v
    }
    fmt.Printf("avg=%.2f\n", float64(sum)/float64(len(scores)))
}

Arrays vs slices (quick contrast)

Arrays copy on assignment; slices are descriptors (ptr, len, cap) referencing an array.

package main
import "fmt"

func modifyArray(a [3]int) { // copy
    a[0] = 999
}

func modifySlice(s []int) { // reference to same backing array
    s[0] = 999
}

func main() {
    arr := [3]int{1, 2, 3}
    modifyArray(arr)
    fmt.Println("array:", arr) // unchanged

    sl := []int{1, 2, 3}
    modifySlice(sl)
    fmt.Println("slice:", sl) // changed
}
  • Convert array to slice: sl := arr[:]
  • Arrays are cheap for small, fixed-size data; slices are flexible and idiomatic for most cases

Multi-dimensional arrays

Nested arrays have fixed sizes at each dimension.

package main
import "fmt"

func main() {
    id := [3][3]int{
        {1, 0, 0},
        {0, 1, 0},
        {0, 0, 1},
    }
    for i := 0; i < 3; i++ {
        fmt.Println(id[i])
    }
}

Comparison and zero values

Arrays of comparable element types can be compared with ==/!=. Zero values fill elements.

package main
import "fmt"

func main() {
    a := [3]int{1, 2, 3}
    b := [3]int{1, 2, 3}
    c := [3]int{1, 2, 4}
    fmt.Println(a == b) // true
    fmt.Println(a == c) // false

    var zero [2]string // ["", ""]
    fmt.Printf("%q\n", zero)
}

Common patterns

Use copies when you want value semantics; pass pointers for efficiency on large arrays.

package main
import "fmt"

// reverse returns a reversed copy
func reverse(a [5]int) [5]int {
    r := a
    for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return r
}

// find returns index of target or -1
func find(a [5]int, target int) int {
    for i, v := range a {
        if v == target {
            return i
        }
    }
    return -1
}

func main() {
    a := [5]int{1, 2, 3, 4, 5}
    fmt.Println(reverse(a))
    fmt.Println(find(a, 3))
}

Gotchas

  • Different lengths are different types; you can’t assign [3]int to [5]int
  • Passing large arrays by value is expensive; prefer slices or pointers
  • Index out of bounds panics; always ensure indices are valid
  • Converting a slice to an array requires matching lengths (Go 1.20+ supports array-to-slice and slice-to-array conversions carefully)

Summary

  • Arrays are fixed-size, value-type, comparable collections
  • Great for small, fixed buffers, lookup tables, and when value semantics matter
  • Convert to slices for flexibility and idiomatic Go