Slices
When PHP code needs a list, it writes $stops[] = 'Bank'; and keeps going. The Go version of that list is a slice, and it's the collection you'll use more than all the others put together.
A slice type looks like an array type with the number left out: []string is "a slice of strings", of any length.
Making one
stops := []string{"Bank", "Angel"} // a literal var later []string // nil counts := make([]int, 8) // eight zeros
The zero value of a slice is nil, and a nil slice is perfectly usable. Its len is 0, range over it does nothing, and you can append to it. So var stops []string is the usual way to start an empty list you're about to fill.
make([]int, 8) builds a slice that already has eight elements, all at their zero value. It's PHP's array_fill(0, 8, 0).
append
There's no $stops[] =. You add to a slice with append, and assign the result back:
stops = append(stops, "Old Street") // several at once stops = append(stops, "Angel", "Euston") // all of another slice, spread as in 1.2 all := append(north, south...)
That assignment isn't optional. Write append(stops, "Old Street") on its own and the build fails with append(stops, "Old Street") (value of type []string) is not used. To see why, you need to know what a slice really is.
What a slice really is
A slice is a small value with three parts: a pointer to an array (the backing array), a length and a capacity. The length is how many elements you can use, len(s). The capacity is how many the backing array has room for, cap(s).
When you append, Go checks for room:
%%{init: {"flowchart": {"nodeSpacing": 18}}}%%
flowchart TB
accTitle: What append does
accDescr: append first checks whether the backing array has spare capacity. If it does, the new value goes into the spare space, and append returns a slice over the same array with the length one bigger. If it doesn't, Go allocates a new, bigger array, usually twice the size, copies the existing values across, adds the new one, and returns a slice over the new array. Either way the result is a new slice value, so you have to assign it.
start["append(stops, x)"] --> q["Room left?<br/>len(stops) < cap(stops)"]
q -->|yes| same["Use the spare space<br/>same array"]
q -->|no| grow["New, bigger array<br/>copy across, add x"]
same --> ret["Return the new slice<br/>stops = append(…)"]
grow --> ret
Either way, the slice you get back is different from the one you passed in: a longer length, and sometimes a whole new array. That's why append returns it, and why you assign it back. Growth roughly doubles the capacity each time, so appending in a loop stays fast.
Slicing
You can take part of a slice with the same [start:end] you used on strings in 1.5. stops[1:3] is indexes 1 and 2, and either side can be left out: stops[:2], stops[2:].
Unlike array_slice, this doesn't copy anything. The new slice points into the same backing array. That's fast, and it's also the source of Go's most surprising slice bugs, so it gets its own lesson next (2.3).
A few PHP habits that don't carry over:
- No negative indexes. The last element is
stops[len(stops)-1]. - Removing the last element is a slice expression,
stops = stops[:len(stops)-1], rather thanarray_pop. - Indexing never grows a slice.
stops[10] = "Bank"on a shorter slice panics withindex out of range. Onlyappendadds elements.
Mind the gap
make with a length, then append, puts your values after the zeros. make([]int, 3) already holds [0 0 0], so appending 7 gives [0 0 0 7]. Either set elements by index (xs[i] = v) or ask make for capacity only: make([]int, 0, 3) has length 0 and room for 3, so append starts at the front. This one turns up in real code all the time, and it's waiting in the exercise.
Tip
var xs []string (nil) and xs := []string{} (empty but not nil) behave the same almost everywhere: same len, same range, same append. The difference shows up when you turn them into JSON, where nil becomes null and empty becomes []. Lesson 7.3 comes back to it.
Recap
[]Tis a slice: a list of any length. Its zero value,nil, is ready to use.- Add with
xs = append(xs, v). Always assign the result. - A slice is a pointer to a backing array, plus a length and a capacity.
appendgrows the array when it runs out of room. make([]T, n)gives you n zero values.make([]T, 0, n)gives you room for n.xs[a:b]shares the original's backing array. More on that in 2.3.