← Назад к вопросам

Что возвращает [ ] для строки?

1.0 Junior🔥 161 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В Go выражение [] для строки возвращает байт, а не символ (руну). Это поведение является одним из ключевых отличий Go от языков с более высокой абстракцией над строками и напрямую связано с внутренним представлением строк.

Основная концепция: строка как срез байт (byte slice)

В Go строка — это иммутабельный (неизменяемый) срез байт ([]byte), представляющий последовательность байтов в кодировке UTF-8. Поэтому оператор индексации s[i] работает на уровне байтов.

package main

import "fmt"

func main() {
    s := "Hello"
    b := s[0]          // b имеет тип byte (uint8)
    fmt.Printf("%c\n", b) // Вывод: H
    fmt.Printf("%v\n", b) // Вывод: 72 (ASCII-код 'H')
}

Важные последствия и примеры

1. Работа с ASCII-строками

Для строк, состоящих только из символов ASCII (каждый символ = 1 байт), индексация ведёт себя "ожидаемо".

s := "abc"
fmt.Println(s[0]) // 97 (байт 'a')
fmt.Println(s[1]) // 98 (байт 'b')

2. Проблема с Unicode (UTF-8) символами

Поскольку Go использует UTF-8, символы могут занимать от 1 до 4 байт. Индексация по байту может "разрезать" многобайтовый символ.

s := "Привет"
b := s[0] // Возвращает первый БАЙТ, а не первую букву 'П'
fmt.Printf("%c\n", b) // Вывод:  (некорректный символ)
// Это байт 0xD0 — первый байт из двухбайтовой последовательности 'П' (0xD0 0x9F)

3. Получение руны (символа) через for range

Чтобы работать именно с символами (рунами), используется цикл for range.

s := "Привет"
for i, r := range s { // r имеет тип rune (int32)
    fmt.Printf("Индекс: %d, Символ: %c, Код: %U\n", i, r, r)
}
// Вывод:
// Индекс: 0, Символ: П, Код: U+041F
// Индекс: 2, Символ: р, Код: U+0440
// Обратите внимание: индекс i увеличивается на 2, 2, 1... байта

Почему так реализовано?

  1. Производительность и простота: Работа на уровне байтов быстрее и соответствует низкоуровневому дизайну Go. Строка — это просто "сырые" байты.
  2. Прозрачность: Разработчик всегда осознаёт, что строка — это байты в UTF-8.
  3. Совместимость: Легко конвертировать в []byte и обратно без перекодировки: []byte(s) и string(b).

Альтернативы для работы с символами

Если требуется получить конкретный символ (руну) по порядковому номеру, а не по байтовому индексу, нужно использовать преобразование в срез рун с осторожностью (это операция O(n) по памяти и времени).

s := "Привет"
runes := []rune(s) // Преобразование всей строки в срез рун
if len(runes) > 0 {
    firstRune := runes[0] // Теперь это руна 'П'
    fmt.Printf("%c\n", firstRune)
}

Важное замечание: Индексация строки s[i] возвращает значение типа byte (uint8), а не rune. Попытка получить доступ к индексу за пределами длины строки вызовет панику (panic).

s := "cat"
val := s[5] // panic: runtime error: index out of range [5] with length 3

Практический вывод

  • s[i] — возвращает байт по i-ой позиции в байтовой последовательности.
  • Для перебора символов всегда используйте for range.
  • Для получения длины в символах используйте utf8.RuneCountInString(s), а не len(s) (последнее возвращает длину в байтах).

Это поведение в Go заставляет разработчика явно думать о кодировке текста, что предотвращает множество скрытых ошибок, связанных с Unicode, которые встречаются в других языках программирования.

Что возвращает [ ] для строки? | PrepBro