Скалярные типы данных
Целочисленные типы
- 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
. - Работа с
[]byte
→bytes.Buffer
. - Максимальная скорость →
copy
в[]byte
.
Константы
Константы вычисляются на тапе компиляции и не меняются в рантайме. Имеют интересную систему типов(чуть отличающуюся от фундаментальных), имеют повышенную точность. Есть генератор констант iota.
Значение по умолчанию
В Go каждый тип имеет значение по умолчанию (zero value), если переменная объявлена, но не инициализирована. Для числовых типов это число 0, для логического типа - false, для строк - пустая строка("").