Скалярные типы данных

Целочисленные типы

  • int8, int16, int32, int64, int
  • uint8, uint16, uint32, uint64, uint
  • byte: синоним типа uint8
  • rune: синоним типа int32

Стоит отметить типы int и uint. Они имеют наиболее эффективный размер для определенной платформы (32 или 64 бита). Это наиболее используемый тип для представления целых чисел в программе. Причем различные компиляторы могут предоставлять различный размер для этих типов даже для одной и той же платформы.

Числа с плавающей точкой(дробные)

  • float32: представляет число с плавающей точкой от 1.410-45 до 3.41038(для положительных). Занимает в памяти 4 байта (32 бита)
  • float64: представляет число с плавающей точкой от 4.910-324 до 1.810308 (для положительных) и занимает 8 байт.

Тип float32 обеспечивает шесть десятичных цифр точности, в то время как точность, обеспечиваемая типом float64, составляет около 15 цифр. В качестве разделителя между целой и дробной частью применяется точка.

Комплексные числа

  • complex64: комплексное число, где вещественная и мнимая части представляют числа float32
  • complex128: комплексное число, где вещественная и мнимая части представляют числа float64

Bool

Логический тип или тип bool может иметь одно из двух значений: true (истина) или false (ложь).

Строки

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

Фактически строка представляет собой срез байтов ([]byte) под капотом, но с важным отличием: строка неизменяема и оптимизирована по памяти.

type string struct {
    data unsafe.Pointer // Указатель на массив байтов
    len  int           // Длина строки
}

С юникод строками удобнее работать как со слайсами из rune

func main() {
   message := "Строка"

    // тут перебираем строку как массив байт
   for i := 0; i < len(message); i++ {
      fmt.Printf("Type: %T, Val: %s\n", message[i], string(message[i])) // Type: uint8, Val: Ð
   }

   // тут как массив рун
   for _, val := range message {
      fmt.Printf("Type: %T, Val: %s\n", val, string(val)) // Type: int32, Val: С
   }
}

Есть альтернативный способ итерации по рунам через utf8.DecodeRuneInString

Можно вручную декодировать руны из байтов:

import "unicode/utf8"

s := "Привет"
for i := 0; i < len(s); {
    r, size := utf8.DecodeRuneInString(s[i:])
    fmt.Printf("Rune: %c (size: %d bytes)\n", r, size)
    i += size
}

Этот способ полезен, если нужно управлять процессом разбиения строки.

Способы построения строк в Go

1. Конкатенация с +

Один из самых простых способов объединения строк в Go — использование оператора +.

s1 := "Hello, "
s2 := "world!"
s3 := s1 + s2 // "Hello, world!"
fmt.Println(s3)

✅ Если строк немного и код должен быть простым и читаемым.

❌ Если строки соединяются в цикле — каждая операция создаёт новую строку в памяти, что неэффективно.


2. fmt.Sprintf

Используется для форматированной вставки значений в строку, аналогично printf в C.

name := "Alice"
age := 30
s := fmt.Sprintf("%s is %d years old", name, age)
fmt.Println(s) // "Alice is 30 years old"

✅ Если нужно объединять строки с форматированием (например, числа, даты).

❌ Если конкатенация идёт в циклеSprintf каждый раз выделяет новую память, что замедляет работу.

3. strings.Builder

Использует буфер в памяти, что делает его эффективным для динамической сборки строк.

var sb strings.Builder
sb.WriteString("Hello")
sb.WriteString(", world!")
fmt.Println(sb.String()) // "Hello, world!"

✅ Если строки объединяются в цикле или динамически, Builder снижает количество аллокаций.

❌ Если строк мало+ проще и читаемее.


4. bytes.Buffer

Работает аналогично strings.Builder, но использует []byte, а не строки.

var buf bytes.Buffer
buf.WriteString("Hello")
buf.Write([]byte(", world!"))
fmt.Println(buf.String()) // "Hello, world!"

✅ Когда работа идёт с []byte (например, обработка бинарных данных, файлов, HTTP-запросов).

❌ Если нужна только строкаstrings.Builder удобнее.


5. copy в []byte (низкоуровневый способ)

Максимально быстрый способ, если заранее известен итоговый размер строки.

parts := []string{"Hello", ", world!"}
length := 0
for _, p := range parts {
    length += len(p)
}

buf := make([]byte, length)
index := 0
for _, p := range parts {
    copy(buf[index:], p)
    index += len(p)
}

s := string(buf)
fmt.Println(s) // "Hello, world!"

✅ Если известен размер строки заранее — позволяет избежать ненужных аллокаций.

❌ Если строки соединяются динамическиstrings.Builder удобнее.

📌 Вывод:

  • Простые случаи+.
  • Форматированиеfmt.Sprintf.
  • Много объединений в циклеstrings.Builder.
  • Работа с []bytebytes.Buffer.
  • Максимальная скоростьcopy в []byte.

Константы

Константы вычисляются на тапе компиляции и не меняются в рантайме. Имеют интересную систему типов(чуть отличающуюся от фундаментальных), имеют повышенную точность. Есть генератор констант iota.

Значение по умолчанию

В Go каждый тип имеет значение по умолчанию (zero value), если переменная объявлена, но не инициализирована. Для числовых типов это число 0, для логического типа - false, для строк - пустая строка("").