Start learning free

LESSON 114 OF 671 · ARRAYS I

Slicing a Slice in Go

Lesson Read the full explanation

TL ; DR:

s[i:j] is a slice with the items from index i up to, not including, j. It shares those items with s.


letters := []string{"a", "b", "c", "d", "e"}

middle := letters[1:4]
fmt.Println(middle)    // [b c d]

fmt.Println(letters)   // [a b c d e]


Full lesson:

Sometimes you need only part of a slice, like the first few items. Slicing gives you that part.

Write the start index and the end index in brackets, with a colon between them. The item at the end index is not included. So letters[1:4] has the items at 1, 2 and 3.

You've seen this before. In Remove Last Item, s[:len(s)-1] dropped the last item.

Leave out the start to begin at index 0. Leave out the end to go to the very end:

fmt.Println(letters[:2])   // [a b]
fmt.Println(letters[3:])   // [d e]

Slicing doesn't change letters. It still has five items.

But a Go slice copies nothing. It shares the same items, so changing middle[0] changes letters[1] too. The next lesson, Slices vs Arrays, shows this in detail.


Your task

runners holds a race's runners, in the order they finished.

Put the first three in a new slice named podium. Put everyone else, from index 3 to the end, in one named rest.

The code below prints both, then the length of runners.

Expected output:

Ana Ben Cleo
Dan Eve
5
STEP 1 OF 2
    Ctrl + Enter to check

    GO · ARRAYS I

    Array Slicing in Go, explained

    Taking part of a slice

    Sometimes you need only part of a slice, such as the first few items. Slicing gives you that part. Write the start index and the end index in brackets with a colon between them. The item at the end index is not included.

    letters := []string{"a", "b", "c", "d", "e"}
    
    middle := letters[1:4]
    fmt.Println(middle)    // [b c d]
    fmt.Println(letters)   // [a b c d e]
    

    letters[1:4] holds the items at indexes 1, 2 and 3. The original slice is unchanged and still has five items. The length of s[i:j] is always j - i.

    Leaving out an index

    Leave out the start to begin at index 0. Leave out the end to run to the very end:

    fmt.Println(letters[:2])   // [a b]
    fmt.Println(letters[3:])   // [d e]
    

    letters[:] is the whole slice, and an empty range such as letters[2:2] is an empty slice with length 0.

    A slice shares its items

    A Go slice copies nothing. It is a window onto the same items. Changing the window changes the original:

    middle[0] = "X"
    fmt.Println(letters)   // [a X c d e]
    

    The same sharing causes a subtler trap. middle has length 3 but capacity 4, because it can grow into the items after it in letters. Calling append on a sub-slice can therefore overwrite items of the original slice. For example, after m := letters[1:4], running m = append(m, "Z") changes letters to [a b c d Z]: the new item was written into the place that used to hold "e". If you need an independent copy, make one explicitly, for example with slices.Clone.

    Length and capacity of a slice

    For middle := letters[1:4], len(middle) is 3 and cap(middle) is 4. The capacity counts from the slice's first item to the end of the memory it points into, which is why it can grow without moving. Most of the time you only need len; capacity matters for the append behaviour described above.

    Dropping items from the ends

    Slicing with len picks items counted from the end, since Go has no negative indexes. On the five letters, letters[:len(letters)-1] is [a b c d], without the last one, and letters[len(letters)-2:] is [d e], the last two. Strings can be sliced the same way: "hello"[1:3] is el.

    To get a copy that doesn't share items, clone the part: after c := slices.Clone(letters[1:4]), setting c[0] = "Q" leaves letters as it was.

    Bounds

    Go checks slice indexes when the program runs, so a bad index is not a compile error; it is a panic that stops the program.

    Both indexes must fit the slice. letters[:6] on a five-item slice panics with slice bounds out of range [:6] with capacity 5. Also, the start can't be larger than the end.

    The task: a race

    runners holds Ana, Ben, Cleo, Dan, Eve in finishing order. The task builds two new slices.

    1. podium is the first three runners: runners[0:3]. Index 3 is not included, so this takes the items at 0, 1 and 2.
    2. rest is everyone else: runners[3:]. With no end index, the slice runs to the end.
    runners := []string{"Ana", "Ben", "Cleo", "Dan", "Eve"}
    podium := runners[0:3]
    rest := runners[3:]
    fmt.Println(podium[0], podium[1], podium[2])
    fmt.Println(rest[0], rest[1])
    fmt.Println(len(runners))
    

    The output is:

    Ana Ben Cleo
    Dan Eve
    5
    

    The last line is 5 because slicing doesn't remove anything from runners.

    Key takeaways

    • s[i:j] holds the items from index i up to, not including, j.
    • Leaving out i means 0; leaving out j means the end.
    • The result shares its items with s, so changes show up in both.
    Keep going with Go500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free