ПрограммированиеGo разработчик

Какие особенности работы со строками (string) в Go нужно знать, чтобы избежать неожиданных ошибок при программировании?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

История вопроса: В 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})) // китайские иероглифы

Ключевые особенности:

  • Строка иммутабельна, нельзя менять по индексу.
  • Строка кодируется в UTF-8, не всегда 1 символ = 1 байт.
  • Преобразование string <-> []byte делает копию.

Вопросы с подвохом.

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.

Типовые ошибки и анти-паттерны

  • Попытка изменить строку напрямую через индексацию.
  • Сравнение длины строки через len — ошибки с юникодом.
  • Использование []byte для сериализации, но забывание про кодировку.
  • Забывание, что string и []byte — разные объекты в памяти.

Пример из жизни

Негативный кейс

У разработчика строка с русскими символами, берут первые 4 байта и ожидают получить первую букву, но выходит только половина символа — получаются "битые" символы.

Плюсы: Получилось быстро и коротко. Минусы: Некорректная работа с Unicode данными, "битые" строки, паника при попытке распарсить такие строки в других местах.

Позитивный кейс

Строки преобразуются в []rune для работы с символами, после нужных действий собирается обратно строка через string(). Работа с []byte делается только для низкоуровневой сериализации, с учетом кодировки.

Плюсы: Корректная обработка Unicode, надежность функций. Минусы: Чуть медленнее, требует больше памяти, но безопасно для любых строк.