История вопроса: В Go строки (string) — базовый и часто используемый тип, активно применяемый для обмена данными, логирования, парсинга и др. Отличие Go в том, что строки иммутабельны, состоят из неизменяемой последовательности байт, и могут содержать данные в UTF-8.
Проблема: Путают работу со строками и байтовыми срезами ([]byte), делают ошибки при изменении строки, при разрезании по рунам и при попытках работать с мультибайтовыми символами (например, кириллица, emoji).
Решение:
Строка иммутабельна, нельзя менять её элементы напрямую — попытка изменить s[0] неправомерна. Строка кодируется в UTF-8, то есть один символ (руна) может быть шире одного байта. Работа с []byte более дешева, но требует ручного контроля. Преобразование string <-> []byte всегда создает копию.
Пример кода:
s := "привет" fmt.Println(len(s)) // 12 байт (кириллица: по 2 байта) fmt.Println(len([]rune(s))) // 6 рун, столько букв fmt.Println(string([]byte{228, 189, 160, 229, 165, 189})) // китайские иероглифы
Ключевые особенности:
1. Можно ли поменять отдельный символ строки через индекс (например, s[1] = 'a')?
Ответ: Нет. Строки неизменяемы, компилятор выдаст ошибку. Нужно создать новый срез []rune или []byte, изменить, затем сконвертировать обратно в строку.
2. Почему len(str) не всегда совпадает с количеством символов в строке?
Ответ: len(str) — это количество байтов, а не рун (символов). Для кириллицы или emoji длинная строка может давать интуитивно неожиданное значение. Для числа символов используйте []rune:
s := "мир 😀" fmt.Println(len(s)) // 7 fmt.Println(len([]rune(s))) // 5
3. Передается ли строка по ссылке или по значению в функцию?
Ответ: По значению, но физически внутри хранится указатель на память и длина. После передачи две переменные "указывают" на один и тот же текст, копия не создается автоматически. Фактическая копия памяти появляется при преобразовании в []byte или из []byte в string.
У разработчика строка с русскими символами, берут первые 4 байта и ожидают получить первую букву, но выходит только половина символа — получаются "битые" символы.
Плюсы: Получилось быстро и коротко. Минусы: Некорректная работа с Unicode данными, "битые" строки, паника при попытке распарсить такие строки в других местах.
Строки преобразуются в []rune для работы с символами, после нужных действий собирается обратно строка через string(). Работа с []byte делается только для низкоуровневой сериализации, с учетом кодировки.
Плюсы: Корректная обработка Unicode, надежность функций. Минусы: Чуть медленнее, требует больше памяти, но безопасно для любых строк.