Strings, bytes and runes
| Strings are byte slices | |
| Range gives runes | |
| strings.Builder | |
| Cast carefully |
Strings are byte slices
A Go string is a read-only run of bytes, usually UTF-8. Indexing gives one byte, len counts bytes, and plain ASCII text hides this until a wider alphabet arrives.
package main
import "fmt"
func main() {
s := "hello"
fmt.Println(len(s))
fmt.Println(s[0])
b := []byte(s)
fmt.Println(b)
}
5
104
[104 101 108 108 111]
Five letters mean five bytes, and s[0] is the number 104, the code for h. Converting to a byte slice exposes the same numbers in a form you can edit.
Range gives runes
Ranging over a string decodes UTF-8 and yields the byte index plus one rune per step. The index jumps by the width of each character, which the numbers below show: every letter of the sample word fills two bytes.
package main
import "fmt"
func main() {
s := "кот"
for i, r := range s {
fmt.Printf("%d %c\n", i, r)
}
fmt.Println(len(s))
}
0 к
2 о
4 т
6
Three letters produce three runes, but six bytes, so the indexes skip ahead by two. Whenever you count characters for people to read, range for runes instead of trusting len.
strings.Builder
Adding strings in a loop with plus copies the growing text again and again. A Builder collects the parts and renders once, which keeps heavy concatenation fast and allocation-light.
package main
import (
"fmt"
"strings"
)
func main() {
var b strings.Builder
for i := 0; i < 3; i++ {
b.WriteString("Go")
}
fmt.Println(b.String())
}
GoGoGo
WriteString appends each piece to an internal buffer, and String renders the result once at the end. For simple joins outside loops, Join and Sprintf stay more readable than a Builder.
Cast carefully
Conversions copy: string from bytes, bytes from string, and runes from string each allocate a fresh value. Convert at the edges of your program, keep the hot path in one type, and never slice a string in the middle of a multibyte character.
package main
import "fmt"
func main() {
b := []byte{104, 105}
fmt.Println(string(b))
r := []rune("кот")
fmt.Println(len(r))
fmt.Println(string(r[0]))
}
hi
3
к
Bytes 104 and 105 become hi, and the rune slice holds three characters regardless of byte width. Round-trip through runes when you must index characters, and stay in bytes for machine-sized work.
Next: Time
Article author: Arthur Isaev