Range of slice expressions

Simple slice expressions

For arrays or strings, the indices are in range if `0 <= low <= high <= len(a)`, otherwise they are out of range.

This is probably intuitive.

``````a := [5]int{1, 2, 3, 4, 5}

a[-1]  // Out of range
s[5:1] // Out of range
a[12]  // Out of range
s[999] // Out of range
``````

For slices, the upper index bound is the slice capacity `cap(a)` rather than the length.

Now this one is not nearly so intuitive. Or useful, IMO… but it means that this code is valid:

``````a := [5]int{1, 2, 3, 4, 5}
s := a[:1] // len(s) == 1, s == []int{1}
s2 := s[:3] // len(s2) == 3, s2 == []int{1, 2, 3}
s3 := s[2:3] // len(s3) == 1, s3 == []int{3}
``````

In other words, it’s possible to take a slice of a slice, and get a result that isn’t even in the original slice at all. 🤯

And this is the explanation for yesterday’s quiz. Full recap and answer below.

A constant index must be non-negative and representable by a value of type `int`; for arrays or constant strings, constant indices must also be in range. If both indices are constant, they must satisfy `low <= high`.

Why do these rules only apply to constants? Well, they only apply to constants at compilation time. For variables, they still apply, but at run-time, which results in a different error condition:

If the indices are out of range at run time, a run-time panic occurs.

Quotes from The Go Programming Language Specification Version of August 2, 2023

Recap of yesterday’s quiz question:

Consider the following program. What is its output?

``````package main

import "fmt"

func main() {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s := a[2:5]
s2 := s[5:7]

fmt.Println(s2)
}
``````
1. `[6 7]`
2. `[8 9]`
3. A runtime panic
4. A compilation error

And as was explained above, the index upper bound for a slice expression of a slice, is the slice’s capacity, not its length. That capacity corresponds to the length of the backing array. So in the case of `s2 := s[...]`, the range for valid indexes is `0..8`, even though `len(s)` is only `2`.

This means the expression `s2 := s[5:7]` refers to `[8 9]`, since its ultimately referencing the backing array (`a`), not `s`.

Confusing, eh? I trust there’s a reason this functionality is in the spec. I’ve never needed it. 🤷