After reading lesson 16, you’ll be able to
Arrays are ordered collections of elements with a fixed length. This lesson uses arrays to store the names of planets and dwarf planets in our solar system, but you can collect anything you like.
Do you have a collection or did you in the past? Maybe stamps, coins, stickers, books, shoes, trophies, movies, or something else?
Arrays are for collecting many of the same type of thing. What collections could you represent with an array?
The following planets array contains exactly eight elements:
var planets [8]string
Every element of an array has the same type. In this case, planets is an array of strings.
Individual elements of an array can be accessed by using square brackets [] with an index that begins at 0, as illustrated in figure 16.1 and shown in listing 16.1.
var planets [8]string planets[0] = "Mercury" 1 planets[1] = "Venus" planets[2] = "Earth" earth := planets[2] 2 fmt.Println(earth) 3
Even though only three planets have been assigned, the planets array has eight elements. The length of an array can be determined with the built-in len function. The other elements contain the zero value for their type, an empty string:
fmt.Println(len(planets)) 1 fmt.Println(planets[3] == "") 2
Go has a handful of built-in functions that don’t require an import statement. The len function can determine the length for a variety of types. In this case it returns the size of the array.
How do you access the first element of the planets array?
What is the default value for elements of a new array of integers?
planets[0]
Elements of an array are initially the zero value for the array’s type, which means 0 for integer arrays.
An eight-element array has indices from 0 through 7. The Go compiler will report an error when it detects access to an element outside of this range:
var planets [8]string planets[8] = "Pluto" 1 pluto := planets[8] 1
If the Go compiler is unable to detect the error, your program may panic while it’s running:
var planets [8]string i := 8 planets[i] = "Pluto" 1 pluto := planets[i] 1
A panic will crash your program, which is still better than modifying memory that doesn’t belong to the planets array, leading to unspecified behavior (as is the case with the C programming language).
A composite literal is a concise syntax to initialize any composite type with the values you want. Rather than declare an array and assign elements one by one, Go’s composite literal syntax will declare and initialize an array in a single step, as shown in the following listing.
dwarfs := [5]string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"}
The curly braces {} contain five comma-separated strings to populate elements of the new array.
With larger arrays, breaking the composite literal across multiple lines can be more readable. And as a convenience, you can ask the Go compiler to count the number of elements in the composite literal by specifying the ellipsis ... instead of a number. The planets array in the following listing still has a fixed length.
planets := [...]string{ 1 "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", 2 }
How many planets are there in listing 16.3? Use the len built-in function to find out.
Iterating through each element of an array is similar to iterating over each character of a string in lesson 9, as shown in the following listing.
dwarfs := [5]string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"} for i := 0; i < len(dwarfs); i++ { dwarf := dwarfs[i] fmt.Println(i, dwarf) }
The range keyword provides an index and value for each element of an array with less code and less chance for mistakes, as shown in the next listing.
dwarfs := [5]string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"} for i, dwarf := range dwarfs { fmt.Println(i, dwarf) }
Both listings 16.4 and 16.5 produce the same output:
0 Ceres 1 Pluto 2 Haumea 3 Makemake 4 Eris
Remember that you can use the blank identifier (underscore) if you don’t need the index variable provided by range.
What mistakes can be avoided by using the range keyword to iterate over an array?
When would it be appropriate to use a conventional for loop instead of range?
With the range keyword, the loop is simpler and avoids mistakes like going out of bounds (for example, i <= len(dwarfs)).
If you need something custom, like iterating in reverse or accessing every second element.
Assigning an array to a new variable or passing it to a function makes a complete copy of its contents, as you can see in the following listing.
planets := [...]string{ "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", } planetsMarkII := planets 1 planets[2] = "whoops" 2 fmt.Println(planets) 3 fmt.Println(planetsMarkII) 4
In the event that you escape the destruction of Earth, you will want Go installed on your own computer. See the instructions at golang.org.
Arrays are values, and functions pass by value, which means the terraform function in the following listing is completely ineffective.
package main import "fmt" // terraform accomplishes nothing func terraform(planets [8]string) { for i := range planets { planets[i] = "New " + planets[i] } } func main() { planets := [...]string{ "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", } terraform(planets) fmt.Println(planets) 1 }
The terraform function is operating on a copy of the planets array, so the modifications don’t affect planets in the main function.
Also, it’s important to recognize that the length of an array is part of its type. The type [8]string and type [5]string are both collections of strings, but they’re two different types. The Go compiler will report an error when attempting to pass an array of a different length:
dwarfs := [5]string{"Ceres", "Pluto", "Haumea", "Makemake", "Eris"} terraform(dwarfs) 1
For these reasons, arrays aren’t used as function parameters nearly as often as slices, covered in the next lesson.
How did Earth survive in planetsMarkII of listing 16.6?
How could listing 16.7 be modified so that the planets array in main is changed?
The planetsMarkII variable received a copy of the planets array, so modifications to either array are independent of each other.
The terraform function could return the revised [8]string array, so that main could reassign planets to the new value. Lesson 17 on slices and lesson 26 on pointers present other alternatives.
You’ve seen arrays of strings, but you can also have arrays of integers, arrays of floating-point numbers, and even arrays of arrays. The 8 × 8 chessboard in the following listing is an array of arrays of strings.
var board [8][8]string 1 board[0][0] = "r" board[0][7] = "r" 2 for column := range board[1] { board[1][column] = "p" } fmt.Print(board)
Consider the game of Sudoku. What would the declaration look like for a 9 × 9 grid of integers?
Let’s see if you got this...