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

В чём разница между строкой и массивом байтов?

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

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

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

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

Строка и массив байтов: ключевые различия

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

Семантическая разница

Строка — это неизменяемая последовательность байтов, интерпретируемая как текст в кодировке UTF-8. Она предназначена для хранения и обработки текстовых данных. Компилятор Go гарантирует, что содержимое строки нельзя изменить после создания.

Массив байтов (срез []byte) — это изменяемая последовательность байтов без привязки к какой-либо кодировке. Он используется для работы с бинарными данными, низкоуровневыми операциями или когда требуется модификация содержимого.

Неизменяемость vs изменяемость

Ключевое отличие — неизменяемость строк. При попытке изменить отдельный символ строки получим ошибку компиляции:

s := "Hello"
s[0] = 'h' // Ошибка компиляции: cannot assign to s[0]

В то время как срез байтов можно свободно модифицировать:

b := []byte("Hello")
b[0] = 'h' // Корректно, b теперь содержит "hello"

Представление в памяти и производительность

Строки и срезы байтов имеют схожее внутреннее представление (указатель на массив байтов + длина), но есть важные нюансы:

// Внутреннее представление строки
type stringStruct struct {
    str unsafe.Pointer
    len int
}

// Внутреннее представление среза
type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

Конвертация между string и []byte требует выделения новой памяти и копирования данных, что создает накладные расходы:

s := "Hello"
b := []byte(s)  // Аллокация и копирование
s2 := string(b) // Ещё одна аллокация и копирование

UTF-8 кодировка и руны

Строки в Go всегда используют UTF-8 кодировку, что позволяет корректно работать с Unicode:

s := "Привет" // UTF-8 кодировка
fmt.Println(len(s)) // 12 байтов, а не 6 символов!

Для подсчета символов нужно конвертировать в руны:

fmt.Println(len([]rune(s))) // 6 символов

Срез байтов же работает на уровне байтов и не гарантирует корректности UTF-8:

b := []byte{0xFF, 0xFE} // Невалидный UTF-8
s := string(b) // Преобразуется, но может вызвать проблемы

Практическое использование

Когда использовать строки:

  • Хранение текстовых данных (сообщения, имена, пути)
  • Ключи в map (только неизменяемые типы)
  • Константы времени компиляции
  • Когда нужна гарантия неизменяемости

Когда использовать []byte:

  • Работа с бинарными протоколами (сети, файлы)
  • Буферизация ввода-вывода
  • Криптографические операции
  • Высокопроизводительная обработка, где нужна модификация на месте

Оптимизации компилятора

Go компилятор выполняет оптимизации для уменьшения накладных расходов:

// Компилятор может избежать копирования в некоторых случаях
var b []byte
// ...
s := string(b) // Иногда не копирует, если b больше не используется

Безопасность и конкурентность

Неизменяемость строк делает их безопасными для использования в конкурентных сценариях без синхронизации. Строку можно свободно передавать между горутинами. Срез байтов требует аккуратного управления доступом при параллельном изменении.

Заключение

Хотя string и []byte взаимопреобразуемы и часто используются вместе, их фундаментальные различия — неизменяемость, семантика UTF-8 и предназначение — определяют выбор в конкретных ситуациях. Понимание этих различий критически важно для написания эффективного, безопасного и идиоматичного Go-кода. Правильный выбор типа влияет на производительность, потребление памяти и корректность работы с текстовыми и бинарными данными.

В чём разница между строкой и массивом байтов? | PrepBro