Slices, maps and strings · 3 MIN READ

A string is bytes; range gives you runes

Index, slice and iterate a string without corrupting characters.

A Go string is an immutable sequence of bytes, commonly containing UTF-8. s[i] returns one byte and len(s) counts bytes.

Ranging over a string decodes UTF-8 and yields each rune with its byte offset. A rune is a Unicode code point, not necessarily a whole displayed character: combining marks and emoji sequences can contain several code points.

SEE THE IDEA

Bytes and Unicode code points are different views

  1. 01

    UTF-8 bytes

    go
    len("é") == 2

    Length counts bytes

  2. 02

    Rune iteration

    go
    for _, r := range text

    range decodes Unicode code points

  3. 03

    Byte index

    go
    text[i]

    Indexing returns one byte

A rune is a code point; a user-visible character may contain several runes.

In practice

Choose the unit the operation actually needs. Protocols often need byte offsets; Unicode processing often needs runes. Range over a string yields the byte offset and the decoded rune, so its indexes are not a consecutive rune counter.

Watch out

Counting runes is still not counting displayed characters. Combining marks and emoji sequences can contain multiple code points.

Official reference ↗

Read and predict

go
package main

import "fmt"

func main() {
	s := "héllo"
	fmt.Println(len(s), len([]rune(s)))
	for i, r := range s {
		if i < 3 {
			fmt.Printf("%d:%c ", i, r)
		}
	}
	fmt.Println()
}

Why do the two lengths differ, and which byte offsets appear?

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.