Перейти к содержимому
Теория · Переменные и типы · урок 4 из 10

Преобразование типов

В прошлом уроке тип был формой коробки. Но что делать, если значение лежит в коробке одной формы, а нужно оно в другой? Его перекладывают, и в Go это всегда делают вручную.

Переменные разных типов нельзя смешивать в одном выражении. Цена товара — дробное число, количество — целое, и перемножить их напрямую не получится:

GO
package main

import "fmt"

func main() {
    count := 3
    price := 2.5
    fmt.Println(count * price) // ✗
}
TEXT
invalid operation: count * price (mismatched types int and float64)

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

Этот вопрос часто задают на собеседовании: «Преобразует ли Go типы сам, неявно?» Ответ — нет. Сумма переменных типов int и float64 не скомпилируется, пока одно из значений не преобразуют явно.

Явное преобразование

Преобразование типа записывается как вызов функции. Запись float64(count) состоит из двух частей:

  • float64 — имя типа, в который нужно переложить значение;
  • (count) — значение в скобках, которое перекладывают.

Результат — новое значение нужного типа. Сама переменная count при этом не меняется: она остаётся int.

Преобразовать можно и целое выражение: float64(a + b) сначала складывает целые числа, а потом превращает сумму в дробную.

Два направления

Между int и float64 значение перекладывают в обе стороны. Направление выбирают так, чтобы ничего не потерять. Поэтому Go и требует писать преобразование явно: так в коде видно место, где может потеряться часть значения.

Из целого в дробное

float64(count) перекладывает целое значение в дробную коробку. Это преобразование исправляет пример с ценой:

GO
count := 3
price := 2.5
total := float64(count) * price
fmt.Println(total) // 7.5

Давайте разберём пример по строкам:

  1. count := 3 — целая коробка с 3.
  2. price := 2.5 — дробная коробка с 2.5.
  3. total := float64(count) * price — здесь float64(count) кладёт копию 3 в дробную коробку, и получается 3.0. Оба множителя теперь типа float64, и 3.0 * 2.5 даёт 7.5.
  4. fmt.Println(total) печатает 7.5.

Это направление выбирают, когда в выражении участвует дробное число и дробная часть результата нужна.

Из дробного в целое

В целой коробке нет места для дробной части. int(x) отбрасывает её, а не округляет. У отрицательного числа она отбрасывается так же, и результат ближе к нулю.

GO
x := 2.9
fmt.Println(int(x), int(-x)) // 2 -2
  1. int(x) отрезает от 2.9 дробную часть и даёт 2.
  2. int(-x) делает то же с -2.9 и даёт -2, а не -3.

Чтобы округлить положительное число до ближайшего целого, прибавьте к нему 0.5 до преобразования: int(x + 0.5) для 2.9 даёт 3.

Ловушка этого направления — потеря на полпути. Запись int(price) * count тоже компилируется, но даёт 6, а не 7.5: дробная часть цены пропадает до умножения.

Здесь картинка с коробками перестаёт работать. Вещь, которая не влезает в коробку, остаётся снаружи целой. А при int(x) лишняя часть числа пропадает насовсем, и Go не предупреждает об этом при запуске.

Числа, записанные в коде

Числа, записанные прямо в коде, ведут себя иначе, чем переменные. У такого числа нет постоянного типа, пока оно не попало в переменную. Сравните два случая:

  • fmt.Println(3 + 1.5) компилируется и печатает 4.5;
  • переменная a := 3 уже имеет тип int, и сложить её с float64 можно только после преобразования.

Частая ошибка

В int преобразовали дробное число, записанное прямо в коде:

GO
fmt.Println(int(2.9)) // ✗
TEXT
cannot convert 2.9 (untyped float constant) to type int

Компилятор видит, что дробная часть такого числа потеряется, и отказывается собирать программу. Отбросить её можно только у значения в переменной, как в примере с int(x).

Главное

  • Go никогда не преобразует типы сам: в арифметической операции оба значения одного типа.
  • Преобразование пишут как вызов, float64(count), а исходная переменная не меняется.
  • int(x) отбрасывает дробную часть, а не округляет: для x := 2.9 получится 2.
  • Число, записанное прямо в коде, не имеет постоянного типа, поэтому 3 + 1.5 компилируется.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом