What slicing does
A slice expression cuts a piece out of a string and gives you a new string. The form is s[start:end]. The result begins at index start and stops just before index end, so start is included and end is excluded. The length of the result is end - start.
In the lesson, message is "Coding feels great". Its indexes run like this:
C o d i n g f e e l s g r e a t
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
The space between words sits at indexes 6 and 12. Each word starts right after a space, and the end bound is the index of the space after it.
Exercise step by step
- The first word.
Codingfills indexes 0 to 5, so the slice ismessage[0:6]. The end bound 6 is the first index left out, which is the space. - The middle word.
feelsstarts at index 7 and ends at index 11, so the slice ismessage[7:12]. - The last word.
greatstarts at index 13 and runs to the end of the string. When the slice goes to the end, you can leave out the end bound:message[13:].
The solution in Go
message := "Coding feels great"
firstWord := message[0:6]
middleWord := message[7:12]
lastWord := message[13:]
Printing the three variables gives:
Coding
feels
great
Details worth knowing
Either bound can be left out
Without start, the slice begins at index 0. Without end, it runs to the end of the string. message[:6] is the same as message[0:6], and message[:] is the whole string.
A slice of a string is still a string
The type of message[0:6] is string, so you can print it, compare it or join it with +. Strings in Go can't be changed, so slicing never alters message; it makes a new value that refers to part of the original text.
Indexes count bytes
Both bounds are byte positions. With ASCII text, as in this lesson, one byte is one character. With text such as "héllo", where é takes two bytes, a slice can cut a letter in half and produce broken text. Convert to []rune first if you need to slice by letters.
Out-of-range bounds stop the program
Some languages quietly cut the slice at the end of the string. Go does not. A bound larger than the length, or a start larger than the end, makes the program panic when it runs:
message := "I love pancakes"
n := 20
fmt.Println(message[:n])
// panic: runtime error: slice bounds out of range [:20] with length 15
The valid range for each bound is 0 up to len(s), and start must not be greater than end. Note that len(s) itself is allowed as a bound, because the end is excluded: s[len(s):] is an empty string.
Common mistakes
- Treating the end as included.
message[0:6]has 6 characters, not 7. If a word comes out one letter short, the end bound is probably one too small. - Off-by-one at the start. The first character is at index 0, so the 8th character is at index 7.
- Using Python-style negative bounds.
message[-5:]doesn't compile in Go. Usemessage[len(message)-5:]for the last five characters. - Counting the spaces wrong. The space is a character and takes an index. Start a word right after it.
Slicing and length together
Because the end bound can be len(s), slicing combines well with the length. For the last word in the lesson, message[13:] and message[13:len(message)] are the same. The last five characters of any string at least five bytes long are s[len(s)-5:]. Taking the first n characters is s[:n], and dropping them is s[n:]. Together, those two slices always rebuild the original string: s[:n] + s[n:] equals s for any n from 0 to len(s).
Checking your work
If a slice looks wrong, print it with quotes using fmt.Printf("%q\n", firstWord). The quotes make a stray leading or trailing space visible, which is the usual sign that a bound is off by one.
Key takeaways
s[start:end]includesstartand excludesend.- Leave out
startto begin at 0 andendto go to the end. - Bounds count bytes and must stay between 0 and
len(s), or the program panics.