Runes
tally reports the longest word in a text. On an English file it works; on a French menu it says café has 5 letters. Nothing is broken — Go is counting bytes, and é takes two of them. Runes are how Go talks about characters instead.
The rule
A rune is one character; a string is bytes; count characters as runes.
A rune is an alias for int32 and holds one Unicode code point. Rune literals use single quotes:
r := 'é'
fmt.Println(r) // 233, the code point
fmt.Printf("%c\n", r) // é
fmt.Println('A') // 65A string is a read-only sequence of bytes, normally UTF-8. In UTF-8, plain English letters take one byte, but é takes two, most other scripts take two or three, and emoji take four. So:
s := "café"
fmt.Println(len(s)) // 5 (bytes)
fmt.Println(utf8.RuneCountInString(s)) // 4 (characters)
fmt.Println(len([]rune(s))) // 4
fmt.Println(s[3]) // 195, a byte, not a characterIndexing a string with s[i] gives a byte. To walk characters, use for range, which decodes UTF-8 for you:
for i, r := range "café" {
fmt.Println(i, string(r))
}
// 0 c
// 1 a
// 2 f
// 3 éi is the byte position where each character starts, and r is the rune. The unicode package works on runes: unicode.IsLetter(r), unicode.ToLower(r).
The shortcut
When code touches text, ask "bytes or characters?" Storage, network sizes and file offsets want bytes (len, s[i]). Anything a human would count wants runes (range, []rune, utf8).
What is asked?
"Longest word" is about letters a person sees — characters.
The trap is s[i] in a loop from 0 to len(s): on non-English text it splits characters in half.
Need to jump to the fifth character directly? Convert once with []rune(s) and index that slice. It costs a copy, so do it when you need random access, not inside a tight loop over a huge file.
Drills
Answer fast — trust the rule.
What type is a rune an alias for?
What does len("naïve") return? (ï takes two bytes in UTF-8.)
What does fmt.Println('A') print?
How many times does for range "héllo" loop?
In for i, r := range "día", what is i for the letter a? (í takes two bytes.)
Remember
- A rune is one character; a string is bytes; count characters as runes.
- Shortcut: bytes for storage, runes for anything a person would count.
- Trap:
len(s)ands[i]are byte-level and split non-English characters.
