Slices, maps and strings · 3 MIN READ

len is what you may read; cap is what you may reslice

Say which of two similar-looking index expressions panics.

A slice has both a length and a capacity, and they gate different things. Indexing is checked against len: s[i] panics the moment i reaches len, however much capacity remains behind it. Reslicing is checked against cap: s[:n] is legal for any n up to cap, and that is how a slice grows back into space it had already allocated.

This is why make([]int, 0, 10) is not indexable at all and make([]int, 10) is: the first has room for ten and holds none.

SEE THE IDEA

Indexing and reslicing use different limits

  1. 01

    Index

    go
    s[i]

    Requires 0 ≤ i < len(s)

  2. 02

    Reslice

    go
    s[:n]

    May extend up to cap(s)

  3. 03

    Full slice

    go
    s[:n:n]

    Also limits future append capacity

Both operations are checked; capacity is not an indexing permission.

In practice

Check bounds at the boundary where external indexes enter your program. A sub-slice capacity starts at its own first element, not at the original array beginning. A full slice expression is useful when passing a view to code that may append without permission to overwrite later elements.

Watch out

Restricting capacity prevents append from using later storage; it does not prevent writes to elements already in the view.

Official reference ↗

Read and predict

go
package main

import "fmt"

func main() {
	s := make([]int, 3, 8)
	fmt.Println(len(s), cap(s))

	grown := s[:6]
	fmt.Println(len(grown), cap(grown))

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("panic on index")
		}
	}()
	fmt.Println(s[5])
}

What does this print?

Try it yourself

Trace each value and write down your reasoning. Copy the source into GopherForge or your local Go environment to compile and check it. This web page does not execute your code or send it to a compiler service.

Progress stays in this browser. No account needed.