Бесплатная миграция IT-инфраструктуры в облако

Типы данных в Go

Илья Ушаков
Илья Ушаков
Технический писатель
03 мая 2023 г.
2986
10 минут чтения
Средний рейтинг статьи: 5

Go (Golang) — многопоточный язык программирования, разработанный в 2009 году компанией Google. Он создан с целью упростить написание высокоэффективного ПО, а его синтаксис схож с языком Си.

Image12

В настоящей инструкции рассмотрим различные типы данных в Go, а также предложим рекомендации по их выбору.

Объявление переменных

Объявление переменной — это процесс ее создания и назначения ей имени и типа данных. В Go переменные могут быть объявлены с помощью ключевого слова var, а также с помощью оператора :=.

Синтаксис объявления переменных с использованием var:

var variable_name data_type

А вот так выглядит объявление переменных через var:

var FirstName string
var salary float32

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

Синтаксис объявления переменных с инициализацией выглядит следующим образом:

var variable_name data_type = value

А вот так выглядит объявление на примерах:

var FirstName string = "Ilya"
var salary float32 = 35000

Кроме всего перечисленного, существует краткий способ объявления переменных с помощью оператора :=. Данный способ автоматически задает тип переменной при указании значения. Синтаксис:

variable_name := initial_value

Примеры:

FirstName  := "Ilya"
salary := 35000

Как видно по картинке ниже, переменной FirstName автоматически присваивается тип string, а переменной salaryint.

Image9

Основные типы данных

В первую очередь, обратим внимание на основные типы данных.

Image10

Целочисленные типы данных

Целочисленные типы данных в Go описывают целочисленные значения, которые могут быть положительными, отрицательными или нулевыми. В Go существует несколько типов целочисленных данных, которые отличаются друг от друга размером в байтах. Рассмотрим каждый из них подробнее в таблице ниже.

Тип

Описание

Диапазон чисел

int8

8-битные числа со знаком. 

от -128 до 127

int16

16-битные числа со знаком. 

от -32,768 до 32,767

int32

32-битные числа со знаком. 

от -231 до 231-1

int64

64-битные числа со знаком. 

от -263 до 263-1

int

Представляет собой 32-битные или 64-битные числа со знаком (в зависимости от платформы). Он используется для работы с целочисленными значениями по умолчанию.

Как у int32 или int64 (в зависимости от платформы)

uint8

8-битные числа без знака.

от 0 до 255

uint16

16-битные числа без знака.

от 0 до 65,535

uint32

32-битные числа без знака.

от 0 до 232-1

uint64

64-битные числа без знака.

от 0 до 264-1

uint

Представляет собой 32-битные или 64-битные числа без знака (в зависимости от платформы).

Как у uint32 или uint64 (в зависимости от платформы)

Кроме перечисленных, еще есть byte и rune. Они эквиваленты uint8 и int32 соответственно.

Примеры объявления целочисленных переменных:

var x int16 = 45000
var y uint = 73000
var z byte = 4

Вещественные типы данных

Вещественные типы данных используются для хранения дробных чисел. В Go предложено 2 варианта: float32 и float64.

  • float32 занимает 4 байта в памяти и может хранить числа с плавающей точкой от -3.4028235E+38 до 3.4028235E+38 с точностью до 7 знаков после запятой; 
  • float64 занимает 8 байт в памяти и может хранить числа с плавающей точкой от -1.7976931348623157E+308 до 1.7976931348623157E+308 с точностью до 15 знаков после запятой.

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

Примеры объявления вещественных переменных:

var x float32 = 3.14
var y float64 = 3.1415926535897

Строковые типы данных

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

Строки являются неизменяемыми (immutable) объектами в Go. Это означает, что после создания строки ее содержимое не может быть изменено. Однако, можно создать новую строку, объединив несколько существующих строк.

В Go также имеются специальные символы. Некоторые из них представлены ниже:

  1. \n — перевод строки. 
  2. \t — табуляция.
  3. \" — двойная кавычка. 
  4. \' — одинарная кавычка. 
  5. \\ — обратный слеш. 

Кроме того, существует возможность использовать Unicode-символы. Для этого можно использовать последовательность символов в формате \uXXXX, где XXXX — это шестнадцатеричное значение Unicode-кода символа.

Примеры объявления строковой переменной:

var exampleText1 string = "Hello, user1!"
exampleText2 := "Hello, user2!"

Логический тип данных

В Go есть логический тип данных bool, который может принимать значения true и false.

Данный тип данных может использоваться для хранения значений логических выражений, например, в условных операторах if и switch, а также в логических операциях, таких как && (логическое И), || (логическое ИЛИ) и ! (логическое НЕ).

Например, мы можем использовать логический тип данных в следующем коде:

age := 18
isAdult := age >= 18
fmt.Println(isAdult)

Результат выполнения кода показан на картинке ниже.

Image7

Составные типы данных

Далее рассмотрим составные типы в Go.

Image11

Массивы

Массивы в Go применяются для хранения фиксированного количества элементов одного типа. Главное отличие от других языков в том, что здесь массивы являются значениями, а не ссылками на данные. 

Для определения массива необходимо указать тип элемента и количество элементов в квадратных скобках. Например:

var exampleArr [4]int = [4]int{2, 5, 7, 9}

Доступ к элементам массива осуществляется по индексу, начиная с 0:

fmt.Println(exampleArr[0])

Массивы в Go также поддерживают итерацию с помощью цикла for:

for i := 0; i < len(exampleArr); i++ {
    fmt.Println(exampleArr[i])
}

Подробнее о циклах for в языке Go мы писали в этой статье.

Срезы

Срезы (Slices) в Go — это ссылочный тип данных, который представляет собой динамический массив элементов, хранящихся в памяти. Они очень похожи на массивы, но в отличие от них, их размер может корректироваться во время выполнения программы, а также они являются ссылками на данные, а не значениями.

Определение среза осуществляется с помощью следующей синтаксической конструкции:

var slice []T

Где T — тип элементов среза, а slice — переменная, хранящая ссылку на срез.

Пустой срез может быть объявлен так:

emptySlice := []int{}

Для инициализации среза можно использовать функцию make, которая создает новый срез заданной длины:

slice := make([]int, 5)

В примере выше создается срез пяти элементов типа int.

Кроме того, функция make имеет третий необязательный параметр, который обозначает вместимость (capacity). Он используется для предварительного указания объема памяти, который будет выделен для хранения элементов среза. Из-за уменьшения количества операций выделения памяти существенно снижается нагрузка на процессор.

Например: 

slice := make([]int, 10, 20)

В данном фрагменте кода создается срез, содержащий 10 элементов с типом данных int и с предварительно выделенной вместимостью на 20 элементов. Это значит, что срез изначально содержит 10 инициализированных нулевыми значениями элементов, и может расширяться до максимум 20 элементов без необходимости выделения дополнительной памяти. Если вдруг количество элементов среза станет больше 20, то Go автоматически выделит новый блок памяти, который будет в 2 раза больше предыдущего.

Подход с параметром capacity рекомендуется использовать, когда пользователю заранее известно максимальное количество элементов, которое потребуется хранить в срезе. 

Чтобы получить доступ к элементам используется индексация, также как и с массивами.

Структуры

Вкратце, структуры — это набор полей. Поля в свою очередь характеризуют различные атрибуты объектов. 

Для объявления структуры в Go используется ключевое слово type, далее указывается имя структуры и ее определение в фигурных скобках. Например, ниже в примере объявим структуру, представляющую информацию о человеке:

type Person struct {
    Name    string
    Age     int
    Address string
}

Для создания переменной, хранящей значения структуры, нужно использовать ключевое слово var и имя переменной. Затем после имени переменной следует имя структуры, за которым следует фигурные скобки со значениями полей структуры в виде имя_поля: значение. Например:

var person1 Person
person1 = Person{Name: "John", Age: 30, Address: "123 Main St."}

Здесь мы объявляем переменную person1 типа Person и присваиваем ей значения для каждого поля. Можно также использовать короткую форму инициализации структуры:

person2 := Person{Name: "Jane", Age: 25, Address: "456 Elm St."}

Доступ к полям структуры осуществляется с помощью оператора точки (.). Например, чтобы получить имя person1, нужно написать:

person1.Name

Структуры могут содержать внутри себя другие структуры и даже ссылки на самих себя (так называемые рекурсивные структуры). Кроме того, они могут реализовывать интерфейсы.

Карты

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

Чтобы создать карту, используется встроенная функция make(), которой передаются типы ключа и значения. Следующий код создает пустую карту с ключами string и значениями int:

m := make(map[string]int)

Чтобы добавить элемент в карту, можно использовать следующую форму записи:

m[key] = value

Где key — ключ элемента, а value — значение элемента. Например:

m["apple"] = 3
m["banana"] = 5

Чтобы получить значение элемента по ключу, используется следующая форма записи:

value := m[key]

Например, для получения значения элемента с ключом apple:

count := m["apple"]

Если элемента с указанным ключом нет в карте, то будет возвращено значение по умолчанию. 

Чтобы удалить элемент из карты, можно использовать встроенную функцию delete(). Следующий код удаляет элемент с ключом apple:

delete(m, "apple")

В Go карты реализованы в виде хеш-таблиц, поэтому порядок элементов в карте не фиксирован и может меняться при каждом выполнении программы. Также следует обратить внимание на то, что карта не является потокобезопасной, поэтому при параллельном доступе к карте необходимо воспользоваться механизмами синхронизации доступа (мьютексы или каналы).

Указатели и интерфейсы

Указатели 

Указатели необходимы для взаимодействия с переменными, хранящими адреса в памяти. Они могут быть определены с помощью символа & перед переменной, а для получения значения, на которое указывает указатель, используется символ *

Пример:

var myInt int = 42
var myIntPointer *int = &myInt
fmt.Println(*myIntPointer) 

Вывод программы показан на картинке ниже.

Image8

Интерфейсы

Интерфейсы служат для определения набора методов, которые должен реализовать тип данных. Интерфейсы могут быть использованы для абстрагирования от конкретного типа данных и для реализации полиморфизма. Чтобы создать интерфейс в Go необходимо определить набор методов, которые должен реализовать тип данных. 

Пример:

type Writer interface {
    Write([]byte) (int, error)
}

Здесь определяется интерфейс Writer, который должен иметь метод Write с аргументом типа []byte и возвращающий количество записанных байтов и ошибку.

Заключение

В данной инструкции мы рассмотрели основные типы данных в Go. Кроме того, для каждого из них мы привели синтаксис объявления и рабочие примеры. 

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

Хотите внести свой вклад?
Участвуйте в нашей контент-программе за
вознаграждение или запросите нужную вам инструкцию
img-server
03 мая 2023 г.
2986
10 минут чтения
Средний рейтинг статьи: 5
Комментарии 2
Дмитрий Алёшин
Дмитрий Алёшин
03.02.2024, 07:16

У make есть третий параметр для срезов (slices), так называемая глубина (capacity) которая обозначает ограничение сверху на количество элементов. Например,

slice := make([]int, 5,100)

создаст срез с 5ю элементами целого типа и максимум 100 элементами. Это тратит память (мы сразу запилили место под 100 элементов), но экономит проц, т.к. нам не нужно делать лишние вычисления. Это удобно, когда примерно понятно сколько будет элементов.

Команда Timeweb Cloud
Команда Timeweb Cloud
05.02.2024, 06:42

Очень полезное уточнение, спасибо. Дополнили статью 👌