Arrays

An array holds a fixed count of values of one type, the count is part of the type, and assigning one copies every element.

Syntaxvar a [N]T // N is a constant; a[i] indexes, len(a) is N

An array holds a fixed number of values of one type, stored one after another. You reach a value by its position, counting from 0. Three integers together is written [3]int.

The length is written into the type. [3]int and [4]int are two different types. A function that takes a [3]int will not accept a [4]int, and the compiler refuses to build it. The length has to be a constant the compiler can see, so you cannot size an array from a number read while the program runs.

Writing one down

var a [3]int gives three zeros, because every element starts at its type's zero value. a := [3]string{"x", "y", "z"} sets them. a := [...]string{"x", "y", "z"} is shorthand for exactly that same type: the compiler counts the values and fills in the 3. The two forms are the same thing. You can also set single positions and leave the rest zero: [5]int{2: 9} is five ints with 9 at index 2.

Arrays are values

Assigning an array copies every element. Passing one to a function copies it too, so the function writes into its own copy. To let a function edit the caller's array, pass its address with &a and take a *[3]int. Two arrays of the same type can be compared with ==, which is true when every element matches.

What trips people up

An index out of range is a compile error when the index is a constant, and a run-time panic when it is a variable. Most Go code stores lists in slices instead, which the next lesson covers.

Example

Example · go
package main

import "fmt"

func main() {
	// Three ints. The 3 is part of the type and can never change.
	var scores [3]int
	scores[0] = 10
	scores[1] = 20
	scores[2] = 30

	// Assignment copies every element, so these two share no memory.
	backup := scores
	backup[0] = 99

	fmt.Println("original:", scores)
	fmt.Println("backup:", backup)
	fmt.Println("length:", len(scores))

	// [...] is shorthand: the compiler counts the values, so this is [3]string.
	days := [...]string{"Mon", "Tue", "Wed"}
	fmt.Println("days:", days, len(days))

	// Same type and same elements means equal.
	fmt.Println("equal:", scores == [3]int{10, 20, 30})

}

// Output:
// original: [10 20 30]
// backup: [99 20 30]
// length: 3
// days: [Mon Tue Wed] 3
// equal: true

When to use it

  • Hold a fixed-size buffer such as a 16-byte key or a 4-byte colour.
  • Model a small board or grid whose dimensions are known while compiling.
  • Hand a small record to a function and be sure the function cannot change yours.
  • Compare two fixed-size values with == without writing a loop.

More examples

Copy in, or pointer in

The only way a function can change the caller's array is to take a pointer to it. Reach for this when a function must fill a fixed-size buffer in place, and note that indexing a pointer to an array needs no explicit star.

Example · go
package main

import "fmt"

// Gets a copy of the three ints, so the caller never sees this write.
func setCopy(a [3]int) {
	a[0] = 1
}

// Gets the address of the array, so this writes the caller's memory.
func setPointer(a *[3]int) {
	// a[0] is shorthand for (*a)[0]. Both forms are allowed.
	a[0] = 1
}

func main() {
	var x [3]int

	setCopy(x)
	fmt.Println(x)

	setPointer(&x)
	fmt.Println(x)

}

// Output:
// [0 0 0]
// [1 0 0]

An array of arrays as a grid

Board games, fixed-size matrices and small lookup tables fit an array of arrays well, because the shape is known while compiling and nothing can resize it by accident.

Example · go
package main

import "fmt"

func main() {
	// Two rows of three columns. Each row is itself a [3]string.
	var grid [2][3]string

	for r := range grid {
		for c := range grid[r] {
			grid[r][c] = "."
		}
	}
	grid[1][2] = "X"

	for _, row := range grid {
		fmt.Println(row)
	}

}

// Output:
// [. . .]
// [. . X]

Returning a changed copy

Copy-on-assignment makes arrays easy to hand around without worrying about shared state, and == compares them element by element. Useful for small fixed records such as a colour or a hash.

Example · go
package main

import "fmt"

// Takes a copy, changes the copy, hands it back. The input is untouched.
func doubled(in [4]int) [4]int {
	out := in // copies all four elements
	for i := range out {
		out[i] *= 2
	}
	return out
}

func main() {
	a := [4]int{1, 2, 3, 4}
	b := doubled(a)

	fmt.Println(a, b, a == b)

}

// Output:
// [1 2 3 4] [2 4 6 8] false

Discussion

  • Be the first to comment on this lesson.