17. Arrays: fixed-size, and rarely what you want
Go has two list-like types, and beginners mix them up constantly. This lesson is the short one: arrays, which have a fixed size baked into their type. The next lesson is about slices, which is what you'll actually use 95% of the time. Understanding arrays first makes slices make sense.
Declaring an array
The length is part of the declaration — and part of the type:
package main
import "fmt"
func main() {
var scores [3]int
fmt.Println(scores)
scores[0] = 90
scores[1] = 85
scores[2] = 78
fmt.Println(scores)
fmt.Println("first:", scores[0], "length:", len(scores))
}
var scores [3]int gives you three ints, all at their zero value. There's
no "empty" array in Go — an array of 3 always has 3 elements, they just start
at zero.
Index from 0, use len() for the length. Reading past the end is a
runtime panic, not a silent nil — try changing scores[0] to
scores[5] and running it.
Literals
package main
import "fmt"
func main() {
primes := [5]int{2, 3, 5, 7, 11}
fmt.Println(primes, len(primes))
days := [...]string{"Mon", "Tue", "Wed"}
fmt.Println(days, len(days))
sparse := [5]int{0: 1, 4: 9}
fmt.Println(sparse)
}
[...] tells the compiler to count the elements for you — it's still a fixed
size, you just didn't have to type it. The third form sets specific indexes
and leaves the rest at zero.
The length is part of the type
This is the thing to remember:
package main
import "fmt"
func main() {
var a [3]int
var b [4]int
fmt.Printf("%T and %T\n", a, b)
// a = b // won't compile: different types
}
[3]int and [4]int are different types. A function that takes a
[3]int cannot be called with a [4]int. That rigidity is exactly why
arrays are impractical for everyday code — you almost never know your data's
length when you write the function.
Arrays are values — they copy
Assign an array and you get a full copy, not a reference:
package main
import "fmt"
func main() {
original := [3]int{1, 2, 3}
copied := original
copied[0] = 999
fmt.Println("original:", original)
fmt.Println("copied: ", copied)
}
original is untouched. The same applies when you pass an array to a
function — it gets its own copy, so a big array means a big copy.
This is the opposite of what you'd expect coming from Python lists or JavaScript arrays, and the opposite of how Go slices behave. Hold onto that contrast; it's the whole point of the next two lessons.
Comparing and ranging
Arrays of comparable elements can be compared directly with ==:
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)
for i, v := range a {
fmt.Printf("index %d holds %d\n", i, v)
}
}
== compares element by element. (Slices, you'll find, can't be compared
this way at all.)
When arrays are actually right
You will meet arrays in a few honest places:
- Fixed-size buffers and cryptographic digests —
[32]bytefor a SHA-256 hash is exactly right, and its fixed size is a feature. - As the backing store for slices — every slice points at an array.
- Where copying semantics are what you want and the size is genuinely known.
Everywhere else: use a slice.
Your turn
Create an array of 4 ints holding 10, 20, 30, 40, then loop over it and
print the running total after each element, one per line:
10
30
60
100
package main
import "fmt"
func main() {
nums := [4]int{10, 20, 30, 40}
// print the running total after each element
}
package main
import "fmt"
func main() {
nums := [4]int{10, 20, 30, 40}
total := 0
for _, n := range nums {
total += n
fmt.Println(total)
}
}
Now for the type you'll actually use every day: slices.