Руна — это code point
Валидация имён сломалась во второй раз — теперь на букве «ё». Счётчик символов показал у «ё» два вместо одного, и имена клиента с умлаутами не прошли проверку. Аня переоткрыла тикет Сергея.
rune (псевдоним int32) — номер символа Unicode до кодирования в байты, а range по строке перебирает именно руны, отдавая байтовый индекс каждого символа.
В прошлом уроке мы работали со строками как с последовательностью байтов. Для ASCII этого достаточно, но с кириллицей такой подход уже не работает: нужна единица «символа». В Go эта единица называется руной.
rune — псевдоним int32, точно так же как byte — псевдоним uint8. Руна хранит один Unicode code point — абстрактный номер символа до того, как его закодировали в байты. Разница между byte и rune в ширине: байт дотягивается до значения 255, а code point'ов в Unicode 1 114 112, поэтому руне нужны 32 бита.
Руна — это число
Что здесь показано
- Стек — локальные переменные функции.
i = 3— значение;p = ptr → 10— указатель на значение;b = "hello"— срез (заголовок: указатель + длина + ёмкость). - Куча — данные, на которые ссылаются переменные:
rodata— байты строки-литерала (в бинарнике),данные N Б— бэкинг-массив слайса,строка— динамическая строка. - Горутины — лёгкие потоки Go: статус (running/runnable/waiting…), функция, в которой каждая сейчас находится, размер стека (растёт по мере работы) и пометка «из main» — горутина запущена из твоего кода.
- GC — сборщик мусора: фаза (off/mark) и число завершённых циклов.
Литерал 'п' — это именно code point 1087, а не его UTF-8-байты. В памяти руна лежит как обычное число, и это число можно печатать, сравнивать, класть в слайс. Два байта 0xD0 0xBF, в которые «п» превращается при кодировании, здесь не участвуют.
Range перебирает руны
Отсюда видно, почему индексация строки отдаёт байт, а range — руну: это два разных действия с разными определениями в спецификации. s[i] — «отдай i-й байт». А range по строке спецификация определяет как обход Unicode code points: на каждом шаге он читает байт, по его старшим битам определяет, сколько байт занимает символ, собирает из них число и отдаёт его как руну. Вместе с руной range отдаёт индекс — но это индекс первого байта символа, а не порядковый номер символа.
Что здесь показано
- Стек — локальные переменные функции.
i = 3— значение;p = ptr → 10— указатель на значение;b = "hello"— срез (заголовок: указатель + длина + ёмкость). - Куча — данные, на которые ссылаются переменные:
rodata— байты строки-литерала (в бинарнике),данные N Б— бэкинг-массив слайса,строка— динамическая строка. - Горутины — лёгкие потоки Go: статус (running/runnable/waiting…), функция, в которой каждая сейчас находится, размер стека (растёт по мере работы) и пометка «из main» — горутина запущена из твоего кода.
- GC — сборщик мусора: фаза (off/mark) и число завершённых циклов.
0 'h'
1 'é'
3 'l'
4 'l'
5 'o'
é занимает два байта, поэтому после него индекс перепрыгивает с 1 сразу на 3. Индексы в цикле байтовые, и резать строку ими можно: s[1:3] — это как раз é. А вот использовать i как номер символа нельзя, для этого нужен отдельный счётчик.
Вы узнаете:
- как первый байт кодирует длину символа в UTF-8
- почему нет O(1)-доступа к i-й руне
Проверьте себя
Что такое rune в Go?
Что перебирает range по строке?
Для s := "héllo" индексы в range идут 0, 1, 3, 4, 5. Почему пропущен 2?
правильных ответов
Байты — это то, как строка хранится и что отдают len и индексация; руны — то, чем оперирует range и что стоит считать символом. Обрабатывая текст, держи оба уровня в голове и проверяй, на каком из них сейчас работаешь.
10 000 ₽ · 60 минут · онлайн