ZetCode

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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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.

main.go
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

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Go tutorials.