Arrays (the fixed kind)

Station 2.1 12 min Interchange: array & SplFixedArray → [N]T

PHP's array is a list, a map, a stack, a queue, a set, a tuple and a DTO. It's also how half your config files end in return [...]. It's one ordered hash map doing every job, and it's very good at it.

Listarray
Maparray
Tuplearray
Config filearray (it's always array)
PHP's array has been working double shifts since 1995.

Go gives each of those jobs its own type, and this unit takes them one at a time:

flowchart LR
  accTitle: PHP's array split into Go's collection types
  accDescr: A single PHP array does four jobs. In Go each job has its own type. A fixed number of items is an array, covered in this lesson. A list that grows is a slice, from lesson 2.2. Key to value lookups are a map, from lesson 2.4. A record with named fields is a struct, from lesson 2.7.
  php["PHP array"]
  php --> arr["array: fixed size"]
  php --> sl["slice: a list that grows"]
  php --> mp["map: key to value"]
  php --> st["struct: named fields"]

Arrays are this lesson, slices are 2.2 and 2.3, maps are 2.4 and 2.5, and structs are 2.7. The one you'll use most is the slice. But a slice is built on top of an array, so arrays come first.

Arrays

A Go array holds a fixed number of values, all of the same type. The number goes in square brackets before the type:

var counts [8]int // eight ints, all 0

stops := [3]string{"Bank", "Moorgate", "Angel"}
days := [...]string{"Mon", "Tue"} // Go counts: [2]string

An array you don't fill in starts with every element at its zero value (1.4), so counts is eight zeros. A literal can list fewer values than the size, and the rest are zero too. [...] asks Go to count the values for you.

Indexing, len and range work as you'd expect from 1.5 and 1.9: stops[0] is "Bank", len(stops) is 3, and for i, s := range stops walks it.

The size is part of the type

[3]string and [4]string are different types. A function that takes an [8]int won't accept a [7]int, and there's no conversion between them. That's what makes arrays awkward for most jobs, since you rarely know the length of a list in advance. It's also why slices exist.

It does mean Go knows every array's length while it compiles, and that gives it something PHP can't do:

Mind the gap

There's no null for a missing index. PHP gives you a warning and null for $stops[10]. In Go, stops[10] with a literal index is a compile error, index 10 out of bounds [0:3]. With a variable index Go can't tell in advance, so it checks when the program runs and panics: index out of range [5] with length 3. A panic stops the program, a bit like an uncaught exception. Unit 4 covers them. Either way, you never get a quiet null.

Arrays are values, just like PHP's

Here's something that behaves exactly as you're used to. Assigning an array copies the whole thing:

a := [3]string{"Bank", "Moorgate", "Old Street"}
b := a // a full copy
b[0] = "Angel"
// a[0] is still "Bank"

PHP arrays work the same way: $b = $a gives you an independent copy. Passing an array to a function copies it too, so the function gets its own copy and can't change yours. And == compares two arrays element by element.

Enjoy the familiarity. Slices, in the next lesson, look almost identical and don't copy like this, and 2.3 is all about what that means.

When you'd actually use one

Not often. Arrays suit things whose size is fixed by their nature: an RGB colour ([3]uint8), the days of the week ([7]string), a board game grid ([8][8]Square), or a SHA-256 hash, which Go's crypto/sha256 hands back as a [32]byte. For everything else, including nearly every list you'd build from data, you'll use a slice.

Recap

  • PHP's array splits into four Go types: arrays, slices, maps and structs.
  • [N]T is a fixed-size array. Unset elements are zero values, and [...] counts a literal for you.
  • The size is part of the type, so [7]int and [8]int don't mix.
  • A bad index is a compile error, or a runtime panic. Never a quiet null.
  • Arrays are values: assigning or passing one copies it, just like PHP's arrays.

1One array, four jobs

PHPjobs.php
<?php
$stops = ['Bank', 'Moorgate'];            // a list
$platforms = ['Bank' => 3, 'Angel' => 1]; // a map
$colour = [255, 211, 41];                 // a fixed tuple
$dep = ['dest' => 'Epping', 'plat' => 3]; // a DTO
GOjobs.go
stops := []string{"Bank", "Moorgate"}         // slice: 2.2
platforms := map[string]int{"Bank": 3}        // map: 2.4
colour := [3]uint8{255, 211, 41}              // array: here
dep := Departure{Dest: "Epping", Platform: 3} // struct: 2.7

Mind the gap

PHP uses one type for all four. Go has a different type for each job, and this unit takes them one at a time. The [], map and struct lines are previews.

2A fixed-size array

PHPfixed.php
<?php
$counts = new SplFixedArray(8);

$counts[0] = 12;
echo $counts->getSize(); // 8
$counts[8] = 1;          // OutOfBoundsException
GOfixed.go
var counts [8]int // eight zeros

counts[0] = 12
fmt.Println(len(counts)) // 8
counts[8] = 1            // compile error: out of bounds

Mind the gap

SplFixedArray is the nearest PHP gets. In Go the size is part of the type, so a constant index that's out of range doesn't even compile.

3Copying

PHPcopy.php
<?php
$a = ['Bank', 'Moorgate', 'Old Street'];
$b = $a;
$b[0] = 'Angel';

echo $a[0]; // "Bank": $a is untouched
GOcopy.go
a := [3]string{"Bank", "Moorgate", "Old Street"}
b := a // copies all three
b[0] = "Angel"

fmt.Println(a[0]) // "Bank": a is untouched

Mind the gap

Something familiar at last: Go arrays are values, just like PHP arrays. Assigning one copies it. Slices, in the next lesson, are where this stops being true.