Доступ к элементам строки и руны
1. Доступ по индексу: s[i] возвращает байт
word := "Go"
fmt.Printf("word[0] = %d\n", word[0]) // 71 (байт)
fmt.Printf("word[0] = %c\n", word[0]) // G (символ)
Важно: s[i] возвращает тип byte (псевдоним uint8), не символ.
2. Проблема с многобайтовыми символами
ASCII работает корректно
word := "Go"
firstByte := word[0] // Тип: byte
fmt.Printf("%c\n", firstByte) // G — корректно
1 символ = 1 байт → всё работает.
Кириллица НЕ работает
russian := "Го"
fmt.Printf("russian[0] = %d\n", russian[0]) // 208
fmt.Printf("russian[0] = %c\n", russian[0]) // Ð — некорректно!
Проблема: Символ "Г" занимает 2 байта в UTF-8.
3. Почему s[0] не работает с кириллицей
Разбор на байты
s := "Г"
bytes := []byte(s)
fmt.Println(bytes) // [208 147]
UTF-8 представление "Г":
Символ: Г
Байты: [0xD0, 0x93] = [208, 147]
russian[0] возвращает только первый байт (208), не весь символ.
4. Решение: использование рун
Преобразование в []rune
russian := "Го"
ruRunes := []rune(russian)
fmt.Printf("Первая руна: %c\n", ruRunes[0]) // Г — корректно!
fmt.Printf("Вторая руна: %c\n", ruRunes[1]) // о
[]rune корректно работает с любыми Unicode-символами.
5. Что такое rune
type rune = int32 // Псевдоним (алиас) для int32
Руна — это Unicode code point, представляющий один символ.
Объявление руны
char := 'А' // Одинарные кавычки — тип rune
fmt.Printf("char = %c\n", char) // А
fmt.Printf("Тип: %T\n", char) // int32
fmt.Printf("Код: %d\n", char) // 1040 (Unicode)
fmt.Printf("Hex: %U\n", char) // U+0410
Одинарные кавычки '...' создают руну, не строку.
6. Сравнение byte vs rune
| Тип | Псевдоним | Размер | Применение |
|---|---|---|---|
byte |
uint8 |
1 байт | ASCII, байты |
rune |
int32 |
4 байта | Unicode символы |
7. Примеры с разными символами
ASCII
s := "Go"
// Способ 1: по байтам (работает)
fmt.Printf("%c\n", s[0]) // G
// Способ 2: через руны (избыточно, но работает)
runes := []rune(s)
fmt.Printf("%c\n", runes[0]) // G
Кириллица
s := "Го"
// ❌ НЕ работает
fmt.Printf("%c\n", s[0]) // Ð (мусор)
// ✅ Работает
runes := []rune(s)
fmt.Printf("%c\n", runes[0]) // Г
Эмодзи
s := "😀"
// ❌ НЕ работает
fmt.Printf("%c\n", s[0]) // � (мусор, 1 из 4 байт)
// ✅ Работает
runes := []rune(s)
fmt.Printf("%c\n", runes[0]) // 😀
8. Итерация по строке
❌ По байтам (некорректно для Unicode)
s := "Привет"
for i := 0; i < len(s); i++ {
fmt.Printf("%c ", s[i]) // Мусор!
}
Выведет байты, не символы.
✅ По рунам через for range
s := "Привет"
for _, r := range s {
fmt.Printf("%c ", r) // П р и в е т
}
for range автоматически итерирует по рунам.
✅ Через []rune
s := "Привет"
runes := []rune(s)
for i := 0; i < len(runes); i++ {
fmt.Printf("%c ", runes[i]) // П р и в е т
}
9. Получение конкретного символа
Неправильно (по байтам)
s := "Привет"
char := s[0] // byte, не символ!
fmt.Printf("%c\n", char) // Ð (первый байт 'П')
Правильно (через руны)
s := "Привет"
runes := []rune(s)
char := runes[0] // rune
fmt.Printf("%c\n", char) // П
10. Типы при обращении
word := "Go"
russian := "Го"
// По умолчанию: byte
b := word[0] // Тип: byte (uint8)
fmt.Printf("Тип: %T\n", b) // uint8
// Явное преобразование в rune
r := rune(word[0])
fmt.Printf("Тип: %T\n", r) // int32
// Для многобайтовых — ТОЛЬКО через []rune
runes := []rune(russian)
r2 := runes[0]
fmt.Printf("Тип: %T\n", r2) // int32
Важно: Присваивание char := s[0] даёт тип byte, не rune.
11. Практические рекомендации
| Задача | Способ |
|---|---|
| ASCII строки | s[i] допустимо |
| Unicode строки | []rune(s)[i] |
| Итерация | for _, r := range s |
| Получение одного символа | runes := []rune(s); runes[i] |
| Работа с байтами (сеть, файлы) | []byte(s)[i] |
12. Итоги
✅ s[i] возвращает байт (тип byte), не символ
✅ Для ASCII s[i] работает корректно
❌ Для Unicode (кириллица, эмодзи) s[i] даёт первый байт, не символ
✅ rune — алиас для int32, представляет Unicode code point
✅ []rune(s) — правильное преобразование для работы с символами
✅ for range s итерирует по рунам, не байтам
✅ Одинарные кавычки 'A' создают rune, двойные "A" — string
⚠️ Будьте внимательны: byte vs rune — разные типы
Ключевое правило:
s[i] → byte (первый байт, может быть некорректным для Unicode)
[]rune(s)[i] → rune (правильный Unicode-символ)
for range s → итерация по рунам
Практический паттерн:
// ❌ Неправильно для Unicode
for i := 0; i < len(s); i++ {
fmt.Printf("%c", s[i]) // Мусор для кириллицы
}
// ✅ Правильно
for _, r := range s {
fmt.Printf("%c", r) // Работает для всех символов
}