Strings, bytes and runes

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