Go array
last modified October 30, 2026
In this article we show how to work with arrays in Go.
An array is a fixed-size collection of elements of the same type.
The length is part of the type: [3]int and [4]int are
different types, and the length cannot change during the life of the array.
Indexes start at zero, and elements that are not initialized explicitly get the
zero value of the element type.
Arrays are value types. Assigning an array to another variable, passing it to a function, or returning it from a function copies all of its elements. In day to day Go code slices are used more often, but arrays are the storage behind them and they have their own uses: fixed-size buffers, lookup tables, and map keys.
All examples in this article were compiled and run with Go 1.27.0.
Declaring an array
An array is declared with the length in square brackets followed by the element type. This example declares an array of two integers, prints it, and assigns both elements.
package main
import "fmt"
func main() {
var vals [2]int
fmt.Println(vals)
vals[0] = 1
vals[1] = 2
fmt.Println(vals)
}
The [2]int is the whole type, including the size. Both elements
start as 0 and are then set to 1 and 2.
$ go run main.go [0 0] [1 2]
Array literals
An array literal lists the elements between curly braces. When fewer values than the length are given, the remaining elements keep their zero value.
package main
import "fmt"
func main() {
vals := [5]int{1, 2, 3, 4, 5}
fmt.Println(vals)
vals2 := [5]int{1, 2, 3}
fmt.Println(vals2)
}
The first array is fully initialized; the second one gets three values and two zeros.
$ go run main.go [1 2 3 4 5] [1 2 3 0 0]
Keyed array literals
A literal can specify the index of each value with index: value.
Indexes that are not mentioned are initialized to zero.
package main
import "fmt"
func main() {
vals := [5]int{1: 6, 2: 7, 4: 9}
fmt.Println(vals)
words := [4]string{0: "falcon", 3: "fox"}
fmt.Println(words)
}
The integers 6, 7 and 9 are placed at indexes 1, 2 and 4. The second literal
leaves the middle strings empty, which is why the empty strings show up as
extra spaces between falcon and fox.
$ go run main.go [0 6 7 0 9] [falcon fox]
Inferring the array length
The ... in place of the length tells the compiler to count the
elements of the literal. The resulting type still has a fixed length.
package main
import (
"fmt"
"reflect"
)
func main() {
vals := [...]int{1, 2, 3, 4, 5, 6}
fmt.Println(vals)
fmt.Println(reflect.TypeOf(vals))
}
The reflect.TypeOf call confirms that the variable is an array of
six integers, not a slice.
$ go run main.go [1 2 3 4 5 6] [6]int
Arrays and slices
The difference between the two types is visible at run time through reflection. An array type has a length, a slice type does not.
package main
import (
"fmt"
"reflect"
)
func main() {
var a [5]int
var b []int
fmt.Println(reflect.ValueOf(a).Kind())
fmt.Println(reflect.ValueOf(b).Kind())
}
a is an array and b is a slice. A slice is a small
header with a pointer to an array, a length and a capacity; the array itself is
the storage.
$ go run main.go array slice
Zero values
An array without an explicit initializer has every element set to the zero value of its type.
package main
import "fmt"
func main() {
var a [3]int
var b [2]string
var c [2]bool
var d [2]float64
fmt.Println(a)
fmt.Printf("%q\n", b)
fmt.Println(c)
fmt.Println(d)
}
Integers print as 0, strings are empty, booleans are false, and floats are 0.
The %q verb makes the two empty strings visible.
$ go run main.go [0 0 0] ["" ""] [false false] [0 0]
Length and capacity
The len function returns the number of elements and
cap returns the same value for an array. Because the length is
known at compile time, len of an array can be used in a constant
expression.
package main
import "fmt"
func main() {
words := [5]string{"falcon", "sky", "earth", "cloud", "fox"}
const size = len(words)
fmt.Println(size, len(words), cap(words))
fmt.Println("There are", len(words), "words in the array")
}
const size = len(words) is valid for an array, but not for a slice,
whose length is only known at run time.
$ go run main.go 5 5 5 There are 5 words in the array
Indexing
Elements are read and written with the index operator. The index must be an
integer between 0 and len(a) - 1.
package main
import "fmt"
func main() {
var words [5]string
words[0] = "falcon"
words[1] = "sky"
words[2] = "earth"
words[3] = "cloud"
words[4] = "fox"
fmt.Println(words[0], words[1])
fmt.Println(words)
}
The first print shows two elements, the second one the whole array. Printing an
array with fmt shows the elements between square brackets.
$ go run main.go falcon sky [falcon sky earth cloud fox]
Slicing an array
A slice expression a[low:high] creates a slice that shares the
storage of the array. The low index is inclusive, the high index is exclusive.
package main
import "fmt"
func main() {
a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]
s[0] = 20
fmt.Println(a)
fmt.Println(s, len(s), cap(s))
}
Changing the first element of the slice also changes the array, because both refer to the same memory. The capacity of the slice reaches from the low index to the end of the array, which is why it is 4 and not 3.
$ go run main.go [1 20 3 4 5] [20 3 4] 3 4
Full slice expression
The three-index form a[low:high:max] sets the capacity to
max - low. This limits how far the slice can grow before
append has to allocate a new array.
package main
import "fmt"
func main() {
a := [5]int{1, 2, 3, 4, 5}
s := a[1:3:3]
s = append(s, 99)
fmt.Println(a)
fmt.Println(s)
}
The slice has length 2 and capacity 2, so appending 99 allocates a new array. The original array is left untouched, which is not the case without the third index.
$ go run main.go [1 2 3 4 5] [2 3 99]
Out-of-bounds index at compile time
When the index is a constant, the compiler checks it against the length of the array and rejects the program if the index lies outside the valid range.
package main
import "fmt"
func main() {
a := [5]int{1, 2, 3, 4, 5}
fmt.Println(a[5])
}
The error message names the valid half-open range, [0:5]. The
program never compiles, so the compiler error is the only output.
$ go run main.go # command-line-arguments ./main.go:9:16: invalid argument: index 5 out of bounds [0:5]
Out-of-bounds index at run time
When the index is a variable, the compiler cannot check its value. The program compiles and panics when the invalid index is used.
package main
import "fmt"
func main() {
a := [5]int{1, 2, 3, 4, 5}
i := 5
fmt.Println(a[i])
}
The panic message reports the offending index and the length of the array.
go run then prints the stack trace and exits with status 2; the
line numbers and offsets in the trace depend on the build and differ between
compiler versions.
$ go run main.go panic: runtime error: index out of range [5] with length 5 goroutine 1 [running]: main.main() /home/user/main.go:10 +0x9 exit status 2
Arrays are value types
Assigning an array to a second variable copies every element. The two variables are independent afterwards.
package main
import "fmt"
func main() {
vals := [5]int{1, 2, 3, 4, 5}
vals2 := vals
vals2[0] = 11
vals2[1] = 22
fmt.Println(vals)
fmt.Println(vals2)
}
The changes made through vals2 do not appear in
vals. This differs from slices, where assignment copies only the
three-word header.
$ go run main.go [1 2 3 4 5] [11 22 3 4 5]
Passing an array to a function
A function parameter is a copy as well. Modifying the parameter inside the function does not modify the caller's array.
package main
import "fmt"
func modify(a [3]int) {
a[0] = 99
fmt.Println("inside:", a)
}
func main() {
a := [3]int{1, 2, 3}
modify(a)
fmt.Println("outside:", a)
}
The function sees its own copy, so the change to 99 disappears when the function returns. For large arrays this copy is also a performance cost.
$ go run main.go inside: [99 2 3] outside: [1 2 3]
Pointers to arrays
To let a function modify the caller's array, pass a pointer to it. Go dereferences the pointer automatically, so the elements are accessed with the usual indexing syntax.
package main
import "fmt"
func modify(a *[3]int) {
a[0] = 99
}
func main() {
a := [3]int{1, 2, 3}
modify(&a)
fmt.Println(a)
}
The array now holds 99 at index 0 after the call. Passing &a
also avoids copying the elements.
$ go run main.go [99 2 3]
Comparing arrays
Arrays are comparable with == when their element type is
comparable. Two arrays are equal when they have the same type, the same length
and equal elements. Comparable arrays can be used as map keys.
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, a == c)
counts := map[[2]string]int{{"a", "b"}: 1}
fmt.Println(counts[[2]string{"a", "b"}])
}
a and b are equal, while c differs in
its last element. The map lookup with an array key finds the stored value.
Arrays containing slices or maps cannot be compared, because those types are
not comparable.
$ go run main.go true false 1
Iterating over an array
A for range loop yields the index and a copy of the element.
Modifying the loop variable does not modify the array.
package main
import "fmt"
func main() {
a := [4]int{1, 2, 3, 4}
for i, v := range a {
v *= 10
fmt.Println(i, v)
}
fmt.Println(a)
}
The printed values are ten times the originals, but the array still holds
1, 2, 3 and 4. To change the elements, assign through the index:
a[i] *= 10.
$ go run main.go 0 10 1 20 2 30 3 40 [1 2 3 4]
Multidimensional arrays
An array can have more than one dimension. The outer array contains arrays as its elements, so each dimension adds a pair of brackets.
package main
import "fmt"
func main() {
var table [3][3]int
for i := 0; i < len(table); i++ {
for j := 0; j < len(table[i]); j++ {
table[i][j] = i * j
}
}
fmt.Println(table)
fmt.Println(table[2][1])
}
The nested loops fill a 3 by 3 multiplication table. The second print reads a single element with two indexes.
$ go run main.go [[0 0 0] [0 1 2] [0 2 4]] 2
Three-dimensional arrays
A third dimension follows the same pattern: another pair of brackets in the type and another level of curly braces in the literal.
package main
import "fmt"
func main() {
a := [3][2][2]int{
{{1, 2}, {3, 4}},
{{5, 6}, {7, 8}},
{{9, 10}, {11, 12}},
}
fmt.Println(a)
fmt.Println(a[2][1][0])
}
The literal lists three blocks of two 2 by 2 matrices. The last print reads the element at index 0 of the second 2 by 2 block inside the third block, which is 11.
$ go run main.go [[[1 2] [3 4]] [[5 6] [7 8]] [[9 10] [11 12]]] 11
Converting a slice to an array
Since Go 1.20 a slice can be converted to an array of the same element type. The conversion copies the elements and panics if the slice is shorter than the array.
package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5}
a := [5]int(s)
fmt.Println(a)
fmt.Println(len(a))
}
The conversion produced a real [5]int with its own storage.
Converting to a longer array, for example [6]int(s), would panic
at run time.
$ go run main.go [1 2 3 4 5] 5
Arrays of structs
The element type of an array can be a struct. Fields are selected through the index of the element.
package main
import "fmt"
type Point struct {
X int
Y int
}
func main() {
points := [2]Point{{1, 2}, {3, 4}}
points[1].Y = 40
fmt.Println(points)
fmt.Println(points[0].X + points[1].X)
}
The assignment points[1].Y = 40 changes the field inside the
array, because indexing an array yields an addressable element. The last print
adds the X fields of both points.
$ go run main.go
[{1 2} {3 40}]
4
Source
Go array types - specification
In this article we have worked with arrays in Go: declaring and initializing them, inferring their length, indexing and slicing, copying and passing them to functions, comparing them, iterating, multidimensional arrays, and converting slices to arrays.
Author
List all Go tutorials.