What is a rune in Go? Comparison of approaches and real examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What is hidden behind the word "rune" in Go? Diving into the world of characters

\n\n

Have you ever encountered Go code where the mysterious word rune appeared? If you come from the world of Python or JavaScript, this might cause slight confusion. "What kind of magical data type is this?" you might ask. Let's figure it out once and for all. This topic is not just academic: understanding runes is the key to working correctly with text, emojis, hieroglyphs, and any characters beyond the English alphabet.

\n\n

This article is not dry documentation, but a living exploration. We will compare approaches to working with characters in Go, Python, and even take a look at C. You will learn why rune in Go is not just a "character", but a powerful tool for working with Unicode, and how to avoid pitfalls when processing strings. Let's go!

\n\n
\n\n

The problem: why is a byte not always a character?

\n\n

Imagine you are writing a program that needs to count the number of characters in a string. A naive approach: take the length of the string. In Python, this works for Latin characters: len("hello") will return 5. But what if the string contains Cyrillic or emojis?

\n\n

In Go (as in most modern languages), strings are stored in UTF-8 encoding. This means that one character can take from 1 to 4 bytes. The English letter 'A' is 1 byte. The Russian 'Я' is 2 bytes. The emoji 🚀 is 4 bytes. If we simply count bytes, we will get an incorrect number of characters.

\n\n

This is where the rune comes onto the scene.

\n\n
\n\n

What is rune in Go? Definition and internal structure

\n\n

Officially: rune is an alias for the int32 type. It represents a Unicode code point. Simply put, it is an integer (from 0 to 0x10FFFF) that uniquely identifies a character in the worldwide Unicode standard.

\n\n
// In Go source code it looks like this:\ntype rune = int32
\n\n

When you write 'A' in single quotes, Go creates a rune (value 65). When you write 'Я' — it's a rune with value 1071 (0x042F). When you write '🚀' — it's a rune with value 128640 (0x1F680).

\n\n

Key difference from byte:

\n
    \n
  • byte — is uint8. Represents one byte (0-255). Used for working with raw data, ASCII characters, or individual bytes of a UTF-8 sequence.
  • \n
  • rune — is int32. Represents one Unicode character, regardless of how many bytes it occupies in memory.
  • \n
\n\n

The main idea: a string in Go is a set of bytes. To get a set of characters (runes) from it, you need to explicitly convert it to a slice of runes ([]rune).

\n\n
\n\n

Comparison of approaches: Go, Python, and C

\n\n

To understand the genius (or unfamiliarity) of Go's approach, let's compare it with other languages.

\n\n

Go: Explicit conversion

\n\n

In Go, you always control the process. A string is bytes. A rune is a character. The transition between them is explicit.

\n\n
package main\n\nimport "fmt"\n\nfunc main() {\n    s := "Привет, мир! 🚀"\n    \n    // Length in bytes\n    fmt.Println("Bytes:", len(s)) // Output: 21\n    \n    // Length in runes (characters)\n    runes := []rune(s)\n    fmt.Println("Runes:", len(runes)) // Output: 13\n    \n    // Iteration over runes (correct way)\n    for i, r := range s {\n        fmt.Printf("Byte index: %d, Rune: %c (U+%04X)\\n", i, r, r)\n    }\n}
\n\n

Note: when iterating with range, Go automatically decodes UTF-8 and gives you runes and their byte indices. This is safe and efficient.

\n\n

Python: Implicit convenience (and hidden pitfalls)

\n\n

In Python 3, strings are sequences of Unicode characters (similar to runes). The length of a string is the number of characters. Seems simpler, right? But there is a nuance.

\n\n
s = "Привет, мир! 🚀"\npri                

Blogs

Book Recommendations