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]intto[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