Strings, Bytes & Runes
Prerequisites: Operators
A Go string is an immutable sequence of bytes, holding
UTF-8 encoded text. Two related types matter:
byte— one byte (an alias foruint8).rune— one Unicode character, or code point (an alias forint32). Rune literals use single quotes:'A','é'.
English letters take one byte each in UTF-8, but characters like
é, ñ, or emoji take two to four — which is why these
distinctions matter.
Syntax
s := "Hello" // interpreted string: supports \n, \t, ...
raw := `C:\temp\new` // raw string: backslashes are literal
b := s[0] // indexing gives a byte, not a character
n := len(s) // length in BYTES
Example
package main
import (
"fmt"
"strings"
"unicode/utf8"
)
func main() {
word := "héllo"
fmt.Println(len(word))
fmt.Println(utf8.RuneCountInString(word))
fmt.Println(word[0], string(word[0]))
for i, r := range word {
fmt.Println(i, string(r))
}
fmt.Println(strings.ToUpper(word))
fmt.Println(strings.Contains(word, "ll"))
fmt.Println(strings.Replace("a-b-c", "-", "+", -1))
fmt.Println(strings.TrimSpace(" padded "))
fmt.Println(strings.Repeat("ab", 3))
}
Expected output
6
5
104 h
0 h
1 é
3 l
4 l
5 o
HÉLLO
true
a+b+c
padded
ababab
Notice what happened:
len(word)is6, not5, becauseétakes two bytes.word[0]is the byte104(the code forh);string(...)turns it back into text.for i, r := range wordwalks the string one rune at a time —iis the byte position andris the rune. That's why the positions jump from1to3. (Theforloop gets its own lesson soon.)
The strings package holds the everyday helpers: Contains,
HasPrefix, HasSuffix, Index, Split, Join, Replace,
ToUpper, ToLower, TrimSpace, Fields, and more.
Common mistakes
- Trying to change a character in place with
s[0] = 'H'. Strings are immutable, so this fails withcannot assign to s[0] (neither addressable nor a map index expression). Build a new string instead (for example withstrings.Replace, or"H" + s[1:]). - Using
len(s)as "number of characters" — for non-ASCII text useutf8.RuneCountInString(s). - Confusing quotes:
"a"is astring,'a'is arune, and backticks make a raw string.
Practice
Given city := "São Paulo", print its length in bytes, its number
of characters, and the city in lowercase.
Show solution
package main
import (
"fmt"
"strings"
"unicode/utf8"
)
func main() {
city := "São Paulo"
fmt.Println(len(city)) // 10
fmt.Println(utf8.RuneCountInString(city)) // 9
fmt.Println(strings.ToLower(city)) // são paulo
}