Search code examples
gosliceindexoutofboundsexceptionsemantics

Slice can access another slice out of range but indexing out of range causes panic


My code:

package main

import (
    "fmt"
)

func main() {
    a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    b := a[1:4]
    fmt.Println("a:", a)
    fmt.Println("b:", b)
    
    // Works fine even though c is indexing past the end of b.
    c := b[4:7]
    fmt.Println("c:", c)
    
    // This fails with panic: runtime error: index out of range [4] with length 3
    // d := b[4]
}

Output:

a: [0 1 2 3 4 5 6 7 8 9]
b: [1 2 3]
c: [5 6 7]

If I uncomment the line that contains d := b[4], it leads to this this error:

panic: runtime error: index out of range [4] with length 3

My question:

Why is it okay to access b[4:7] even though the index 4 is out of range for b which has a length 3 but it is not okay to access b[4]? What Go language rules explain this behavior?


Solution

  • Relevant rules: Spec: Index expressions and Spec: Slice expressions.

    In short: when indexing, index must be less than the length. When slicing, upper index must be less than or equal to the capacity.

    When indexing: a[x]

    the index x is in range if 0 <= x < len(a), otherwise it is out of range

    When slicing: a[low: high]

    For arrays or strings, the indices are in range if 0 <= low <= high <= len(a), otherwise they are out of range. For slices, the upper index bound is the slice capacity cap(a) rather than the length.

    When you do this:

    b := a[1:4]
    

    b will be a slice sharing the backing array with a, b's length will be 3 and its capacity will be 9. So later it is perfectly valid to slice b even beyond its length, up to its capacity which is 9. But when indexing, you can always index only the part covered by the slice's length.

    We use indexing to access current elements of a slice or array, and we use slicing if we want to create a fragment of an array or slice, or if we want to extend it. Extending it means we want a bigger portion (but what is still covered by the backing array).