Строки и руны

Почему строка в Go — это байты, что такое руна и как правильно считать символы.

Руна (rune) — это один символ Unicode (кодовая точка), псевдоним для int32. Строка же хранится как последовательность байтов UTF-8.

Строки неизменяемы и хранят байты

Строка в Go — это неизменяемая последовательность байтов, обычно в кодировке UTF-8. Длина len(s) возвращает число байтов, а не символов. Для латиницы это одно и то же, но кириллица и эмодзи занимают несколько байтов каждый.

package main

import "fmt"

func main() {
    s := "Go"
    r := "Привет"
    fmt.Println(len(s)) // 2 байта
    fmt.Println(len(r)) // 12 байт: каждая буква по 2 байта
}

Вывод:

2
12

Поэтому len("Привет") — это 12, а не 6. Это типичная ловушка для тех, кто привык к строкам как к массиву символов.

Руны: считаем символы правильно

Чтобы работать с символами, а не байтами, строку перебирают через for range — он автоматически декодирует UTF-8 и выдаёт руны. А для подсчёта символов есть utf8.RuneCountInString.

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "Привет"
    fmt.Println(utf8.RuneCountInString(s)) // 6 символов

    for i, r := range s {
        fmt.Printf("%d: %c\n", i, r)
    }
}

Вывод:

6
0: П
2: р
4: и
6: в
8: е
10: т

Обратите внимание: индексы идут 0, 2, 4… — это байтовые смещения, ведь каждая кириллическая руна занимает по 2 байта. Глагол %c печатает руну как символ.

Преобразования: []byte и []rune

Строку можно превратить в срез байтов или срез рун и обратно. []rune удобен, когда нужен доступ к символу по индексу или разворот строки.

package main

import "fmt"

func main() {
    s := "Go"
    bytes := []byte(s)  // [71 111]
    runes := []rune("Дом")
    fmt.Println(bytes)
    fmt.Println(string(runes[0])) // первый символ
    fmt.Println(len(runes))       // 3 символа
}

Вывод:

[71 111]
Д
3
ВыражениеСмысл
len(s)число байтов
utf8.RuneCountInString(s)число символов
[]byte(s)срез байтов
[]rune(s)срез символов (рун)

Итог

  • Строка — неизменяемые байты UTF-8; len считает байты, не символы.
  • Руна (rune) — символ Unicode; перебор через for range даёт руны.
  • Символы считают через utf8.RuneCountInString, доступ по индексу — через []rune.
Проверьте себя
1. Что возвращает len(s) для строки?
AЧисло символов
BЧисло байтов
CЧисло слов
DРазмер в килобайтах
2. Что такое rune в Go?
AОдин байт строки
BСимвол Unicode (кодовая точка), псевдоним int32
CТип для случайных чисел
DУказатель на строку
3. Как правильно посчитать число символов в строке с кириллицей?
Alen(s)
Butf8.RuneCountInString(s)
Ccap(s)
Dsize(s)
Поддержать проект