Skip to content
AgileLogics
6 min read

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 for uint8).
  • rune — one Unicode character, or code point (an alias for int32). 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) is 6, not 5, because é takes two bytes.
  • word[0] is the byte 104 (the code for h); string(...) turns it back into text.
  • for i, r := range word walks the string one rune at a time — i is the byte position and r is the rune. That's why the positions jump from 1 to 3. (The for loop 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 with cannot assign to s[0] (neither addressable nor a map index expression). Build a new string instead (for example with strings.Replace, or "H" + s[1:]).
  • Using len(s) as "number of characters" — for non-ASCII text use utf8.RuneCountInString(s).
  • Confusing quotes: "a" is a string, 'a' is a rune, 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
}