Базовые типы Go это числа, булевы значения и строки. У каждой переменной ровно один тип, зафиксированный на этапе компиляции, и Go никогда не преобразует типы за вас. %T печатает тип любого значения:
Обратите внимание: 'G' печатается как 71 с типом int32. Символ в одинарных кавычках это руна, то есть число (кодовая точка Unicode). rune это псевдоним int32 (тот же тип под вторым именем), поэтому %T сообщает int32. Точно так же byte показывается как uint8.
Целые числа
| Тип | Размер | Диапазон |
|---|---|---|
int8 | 8 бит | от -128 до 127 |
int16 | 16 бит | от -32 768 до 32 767 |
int32 | 32 бита | примерно от -2,1 млрд до 2,1 млрд |
int64 | 64 бита | примерно от -9,2 квинтиллиона до 9,2 квинтиллиона |
uint8 (byte) | 8 бит | от 0 до 255 |
uint16 | 16 бит | от 0 до 65 535 |
uint32 | 32 бита | от 0 примерно до 4,3 млрд |
uint64 | 64 бита | от 0 примерно до 18,4 квинтиллиона |
int, uint | 32 или 64 бита | размер машинного слова платформы |
uintptr | размер платформы | хранит значение указателя, для низкоуровневого кода |
По умолчанию используйте int. len() возвращает int, индексы слайсов имеют тип int, счётчики циклов тоже int. На любой 64-битной платформе int занимает 64 бита. Типы с фиксированным размером нужны, когда размер входит в контракт: int32 в бинарном формате файла, uint8 для сырых байтов, int64 для Unix-времени в наносекундах.
Не используйте беззнаковые типы для величин, которые «не могут быть отрицательными», например для количества. Вычитание ниже нуля даёт огромное число, а не отрицательное, которое можно было бы поймать.
Пределы и переполнение
В пакете math есть константа для каждого предела. Во время выполнения переполнение целочисленной арифметики молча заворачивается по кругу:
Ни паники, ни ошибки: 127 + 1 превращается в -128. На этапе компиляции проверяются только константные выражения (var b int8 = 128 не скомпилируется). Если в ваших данных переполнение возможно, проверяйте перед операцией или используйте math/big.
Целочисленные литералы
million := 1_000_000 // underscores for readability (Go 1.13+)
mask := 0xFF // hexadecimal
perm := 0o755 // octal (also the older form 0755)
flags := 0b1010 // binary
Числа с плавающей точкой
| Тип | Размер | Точность |
|---|---|---|
float32 | 32 бита | примерно от 6 до 9 значащих цифр |
float64 | 64 бита | примерно от 15 до 17 значащих цифр |
Литерал с десятичной точкой или экспонентой (2.5, 1e6) по умолчанию имеет тип float64, и пакет math принимает и возвращает float64. Используйте его, если только память в большом массиве не критична.
Числа с плавающей точкой двоичные, поэтому большинство десятичных дробей хранится приближённо:
Отсюда два правила. Никогда не сравнивайте float через == после арифметики. И никогда не храните деньги во float: используйте целое число копеек или библиотеку десятичных чисел.
Деление float на ноль даёт +Inf, -Inf или NaN вместо паники. Деление целого на ноль вызывает панику во время выполнения (runtime error: integer divide by zero), а константный нулевой делитель это ошибка компиляции.
Булевы значения
bool хранит true или false, нулевое значение false. Go не считает числа, пустые строки или nil булевыми значениями. if count {} это ошибка компиляции; пишите if count > 0 {}. Операторы: &&, || и !, причём && и || вычисляются по короткой схеме.
Строки
string это неизменяемая последовательность байтов, обычно текст в UTF-8. len(s) считает байты, а не символы:
é занимает в UTF-8 два байта, поэтому len сообщает 6 для пяти символов. Строки в двойных кавычках обрабатывают escape-последовательности вроде \n; сырые строки в обратных кавычках их не обрабатывают и могут занимать несколько строк. Пакет strings разобран на странице о строках, а UTF-8 подробно описан на странице о рунах и байтах.
byte и rune
| Псевдоним | То же, что | Хранит |
|---|---|---|
byte | uint8 | один байт сырых данных |
rune | int32 | одну кодовую точку Unicode |
Это псевдонимы, а не новые типы: byte и uint8 взаимозаменяемы без преобразования. Имена лишь говорят, что означает число.
Комплексные числа
В Go есть встроенные комплексные типы complex64 и complex128, функции real, imag и пакет math/cmplx:
Они нужны немногим программам, но в обработке сигналов и некоторых численных задачах без них не обойтись.
Составные и ссылочные типы
Базовые типы складываются в составные. У каждого есть своя страница:
| Тип | Пример | Нулевое значение |
|---|---|---|
| массив | [3]int | три нуля |
| слайс | []int | nil |
| мапа | map[string]int | nil |
| структура | struct{ X, Y int } | все поля обнулены |
| указатель | *int | nil |
| функция | func(int) int | nil |
| канал | chan int | nil |
| интерфейс | error, any | nil |
any это псевдоним interface{} (с Go 1.18), он может хранить значение любого типа.
Именованные типы
Можно определить новый тип на основе существующего. Представление у него то же, но это отдельный тип, и у него могут быть методы:
type Celsius float64
type UserID int64
var t Celsius = 21.5
var f float64 = t // compile error: cannot use t (variable of float64 type Celsius) as float64 value in variable declaration
В этой строгости и смысл: UserID нельзя случайно передать туда, где ожидается OrderID. Если преобразование задумано, пишите его явно: float64(t). Правила описаны на странице о преобразовании типов, там же функции strconv для перехода между числами и строками.
Нулевые значения
У каждого типа есть нулевое значение, которое хранит переменная, пока вы ничего не присвоили: 0 для чисел, false для bool, "" для строк и nil для указателей, слайсов, мап, каналов, функций и интерфейсов. Неинициализированных переменных в Go нет.
Часто задаваемые вопросы
Какой размер у int в Go?
int и uint занимают 64 бита на 64-битных платформах (amd64, arm64) и 32 бита на 32-битных. Если нужен фиксированный размер, например для формата файла или сетевого протокола, используйте int32, int64, uint8 и так далее. Для обычных счётчиков, индексов и длин берите int.
Как вывести тип переменной в Go?
Используйте глагол %T: fmt.Printf("%T\n", x) печатает int, float64, []string, main.User и так далее. В коде reflect.TypeOf(x) возвращает ту же информацию в виде значения, которое можно исследовать.
Чем byte отличается от rune в Go?
byte это псевдоним uint8, он хранит один байт сырых данных. rune это псевдоним int32, он хранит одну кодовую точку Unicode. Строка это последовательность байтов; обход через for range декодирует её в руны.
Что использовать в Go: float32 или float64?
float64, если нет причин поступить иначе. Это тип по умолчанию для литералов с плавающей точкой, с ним работает пакет math, и его точность примерно от 15 до 17 значащих десятичных цифр против примерно от 6 до 9 у float32. float32 выбирайте, только чтобы вдвое сократить память в больших массивах или чтобы соответствовать внешнему формату.
Какое максимальное значение int в Go?
Используйте константы пакета math: math.MaxInt (9223372036854775807 на 64-битных платформах), math.MinInt, math.MaxInt64, math.MaxUint32 и так далее. Арифметика, вышедшая за предел, во время выполнения молча переполняется по кругу; паники не будет.