Skip to main content

Какие существуют встроенные типы данных в Go?

Встроенные типы данных в Go

В Go типы можно разделить на несколько основных категорий: логические, числовые, строковые, составные и специальные.

1. Логический тип

bool

Хранит одно из двух значений: true или false.

var isActive bool = true
var isReady bool = false

Нулевое значение типа boolfalse.

2. Числовые типы

Целые числа со знаком

Могут хранить положительные, отрицательные числа и ноль.

ТипРазмерДиапазон
int81 байтот -128 до 127
int162 байтаот -32 768 до 32 767
int324 байтапримерно от -2,1 млрд до 2,1 млрд
int648 байтпримерно от -9,2 × 10¹⁸ до 9,2 × 10¹⁸
int4 или 8 байтзависит от платформы и реализации
var age int = 30
var bigNumber int64 = 9_000_000_000

int обычно имеет размер 32 бита на 32-битных системах и 64 бита на 64-битных системах.

При этом int и int64 остаются разными типами, даже если занимают одинаковое количество памяти:

var a int = 10
var b int64 = int64(a)

Без явного преобразования присвоить int в int64 нельзя.

Целые числа без знака

Хранят только положительные числа и ноль.

ТипРазмерДиапазон
uint81 байтот 0 до 255
uint162 байтаот 0 до 65 535
uint324 байтаот 0 до примерно 4,2 млрд
uint648 байтот 0 до примерно 1,8 × 10¹⁹
uint4 или 8 байтзависит от платформы
uintptrразмер указателяиспользуется для низкоуровневой работы с адресами
var count uint = 100
var port uint16 = 8080

uintptr не следует использовать как обычный указатель. Он нужен в основном для взаимодействия с низкоуровневым кодом и пакетом unsafe.

Числа с плавающей точкой

Используются для хранения дробных чисел.

ТипРазмерПримерная точность
float324 байтаоколо 7 значащих цифр
float648 байтоколо 15–16 значащих цифр
var price float64 = 19.99
var pi float32 = 3.14159

Такие числа хранятся приблизительно в виде:

знак × значащая часть × 2^экспонента

Из-за двоичного формата многие десятичные дроби нельзя представить абсолютно точно.

a := 0.1
b := 0.2

fmt.Println(a + b)       // 0.30000000000000004
fmt.Println(a+b == 0.3)  // false

Для денежных значений лучше использовать:

priceInCents := 1999

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

Комплексное число состоит из действительной и мнимой частей.

ТипЧасти числа
complex64две части типа float32
complex128две части типа float64
var c1 complex64 = 1 + 2i
var c2 complex128 = 3 + 4i

Используются в основном в математических и научных расчётах.

3. Строки и типы для работы с текстом

string

string — неизменяемая последовательность байтов.

var name string = "Привет"

Строка может содержать любые байты. При работе с текстом обычно используется кодировка UTF-8, но Go не требует, чтобы каждая строка обязательно содержала корректный UTF-8.

Функция len возвращает количество байтов, а не количество символов:

name := "Привет"

fmt.Println(len(name)) // 12

Каждая русская буква в UTF-8 занимает два байта.

Строки неизменяемы: нельзя заменить отдельный байт строки.

s := "hello"

// s[0] = 'H' // ошибка компиляции

s = "Hello"

При обращении по индексу возвращается байт:

s := "Hello"

fmt.Println(s[0])       // 72
fmt.Println(string(s[0])) // H

byte

byte — псевдоним для uint8.

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

var b byte = 'A'

fmt.Println(b)         // 65
fmt.Println(string(b)) // A

rune

rune — псевдоним для int32.

Обычно представляет одну кодовую точку Unicode.

var r rune = 'Я'

fmt.Println(r)         // 1071
fmt.Println(string(r)) // Я

Важно: rune не всегда равна одному визуальному символу. Некоторые отображаемые символы могут состоять из нескольких кодовых точек Unicode.

Для перебора строки по rune используется range:

for index, r := range "Привет" {
	fmt.Println(index, r, string(r))
}

4. Составные типы

Массив

Массив хранит фиксированное количество элементов одного типа.

var nums [3]int = [3]int{10, 20, 30}

nums[0] = 5

Размер массива является частью его типа:

var a [3]int
var b [4]int

[3]int и [4]int — разные типы.

Массивы в Go копируются при присваивании:

a := [3]int{1, 2, 3}
b := a

b[0] = 100

fmt.Println(a) // [1 2 3]
fmt.Println(b) // [100 2 3]

Слайс

Слайс описывает участок базового массива.

Он хранит:

nums := []int{1, 2, 3}

nums = append(nums, 4)

Слайс не имеет фиксированного размера:

fmt.Println(len(nums)) // длина
fmt.Println(cap(nums)) // ёмкость

append может использовать существующий базовый массив или создать новый, если текущей ёмкости недостаточно.

nums := make([]int, 0, 3)

nums = append(nums, 10)
nums = append(nums, 20)

Мапа

Мапа хранит пары «ключ — значение».

ages := map[string]int{
	"Alice": 30,
	"Bob":   25,
}

fmt.Println(ages["Alice"]) // 30

Получение значения по ключу:

age, exists := ages["Alice"]

if exists {
	fmt.Println(age)
}

Ключом мапы может быть только сравнимый тип.

Допустимые ключи:

map[string]int{}
map[int]string{}
map[[2]int]string{}

Недопустимые ключи:

// map[[]int]string{}        // ошибка
// map[map[string]int]int{}  // ошибка
// map[func()]string{}       // ошибка

Слайсы, мапы и функции нельзя использовать как ключи.

Структура

Структура объединяет несколько именованных полей в один тип.

type User struct {
	Name string
	Age  int
}

u := User{
	Name: "Иван",
	Age:  25,
}

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

func (u User) Greeting() string {
	return "Hello, " + u.Name
}

Использование:

fmt.Println(u.Greeting())

5. Указатели и функциональные типы

Указатель

Указатель хранит адрес другого значения в памяти.

x := 10
p := &x

Оператор & возвращает адрес значения:

p := &x

Оператор * получает значение по адресу:

fmt.Println(*p) // 10

Через указатель можно изменить исходное значение:

*p = 20

fmt.Println(x) // 20

Нулевое значение указателя — nil.

var p *int

fmt.Println(p == nil) // true

Функция

Функции в Go являются значениями.

Их можно:

add := func(a, b int) int {
	return a + b
}

fmt.Println(add(2, 3)) // 5

Можно объявить собственный тип функции:

type Operation func(int, int) int
var op Operation = add

6. Каналы

chan

Канал используется для передачи данных между горутинами.

Горутина — лёгкая функция, которая может выполняться одновременно с другими функциями.

ch := make(chan int)

go func() {
	ch <- 42
}()

value := <-ch

fmt.Println(value) // 42

Тип канала определяет, какие данные через него можно передавать:

chan int
chan string
chan User

Каналы могут быть:

chan int   // двунаправленный канал
chan<- int // только для отправки
<-chan int // только для получения

7. Интерфейсы

interface

Интерфейс описывает набор методов.

type Stringer interface {
	String() string
}

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

type User struct {
	Name string
}

func (u User) String() string {
	return u.Name
}

Теперь User удовлетворяет интерфейсу Stringer.

var s Stringer = User{Name: "Alice"}

Явно указывать, что тип реализует интерфейс, не требуется.

any

any — псевдоним для пустого интерфейса:

interface{}

Он может хранить значение любого типа.

var value any

value = 42
value = "hello"
value = true

Чтобы получить конкретный тип, используется проверка типа:

text, ok := value.(string)

if ok {
	fmt.Println(text)
}

8. Встроенный интерфейс error

error используется для представления ошибок.

Он объявлен примерно так:

type error interface {
	Error() string
}

Пример:

func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("division by zero")
	}

	return a / b, nil
}

Использование:

result, err := divide(10, 0)

if err != nil {
	fmt.Println(err)
	return
}

fmt.Println(result)

9. Псевдонимы и специальные значения

ИмяЗначение
byteпсевдоним uint8
runeпсевдоним int32
anyпсевдоним interface{}
errorвстроенный интерфейс для ошибок
nilотсутствие значения для некоторых типов
trueбулева константа
falseбулева константа

nil может быть значением для:

var pointer *int
var slice []int
var users map[string]int
var ch chan int
var handler func()
var value any

Все эти переменные изначально имеют значение nil.

10. Нулевые значения

Если переменная объявлена без начального значения, Go автоматически присваивает ей нулевое значение соответствующего типа.

var i int
var f float64
var b bool
var s string
var p *int

Значения:

i == 0
f == 0.0
b == false
s == ""
p == nil

Для составных типов:

var arr [3]int
var slice []int
var users map[string]int
var user User

Нулевые значения:

arr   == [0 0 0]
slice == nil
users == nil

Все поля структуры также получают нулевые значения своих типов.

Важно: нулевая мапа равна nil. Из неё можно читать, но в неё нельзя записывать.

var users map[string]int

fmt.Println(users["Alice"]) // 0

// users["Alice"] = 30 // panic

Перед записью мапу нужно создать:

users = make(map[string]int)
users["Alice"] = 30

Нулевой слайс тоже равен nil, но с ним можно использовать append:

var nums []int

nums = append(nums, 10)

Краткая шпаргалка

// Логический тип
bool

// Строка
string

// Целые числа со знаком
int
int8
int16
int32
int64

// Целые числа без знака
uint
uint8
uint16
uint32
uint64
uintptr

// Дробные числа
float32
float64

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

// Псевдонимы
byte // uint8
rune // int32
any  // interface{}

// Составные типы
[N]T        // массив
[]T         // слайс
map[K]V     // мапа
struct      // структура

// Другие типы
*T          // указатель
func        // функция
chan T      // канал
interface   // интерфейс

Итог

Основные встроенные типы Go:

Кроме них, Go предоставляет псевдонимы byte, rune, any, а также встроенный интерфейс error.