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.
var a [N]T // N is a constant; a[i] indexes, len(a) is NAn 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
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.
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.
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.
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