Skip to content
Theory · Variables and types · lesson 4 of 10

Type conversions

In the previous lesson a type was the shape of a box. But what if a value sits in a box of one shape and you need it in another? You move it over, and in Go you always do that by hand.

Variables of different types cannot be mixed in one expression. The price of an item is a fractional number, the count is a whole one, and multiplying them directly does not work:

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)

Some languages turn the whole number into a fractional one on their own in this case. Go never does that: both values in an arithmetic operation must have the same type. The program does not build until the types match.

Interviewers like to ask this: "Does Go convert types implicitly?" The answer is no. The sum of an int variable and a float64 variable does not compile until one of the values is converted explicitly.

Explicit conversion

A type conversion is written like a function call. The expression float64(count) has two parts:

  • float64 is the name of the type to move the value into;
  • (count) is the value in parentheses that gets moved.

The result is a new value of the needed type. The variable count itself does not change: it stays an int.

A whole expression can be converted too: float64(a + b) adds the integers first and then makes the sum fractional.

Two directions

Between int and float64, a value moves both ways. The direction is chosen so that nothing is lost. That is why Go requires an explicit conversion: it keeps visible the place where part of a value may be lost.

From whole to fractional

float64(count) moves a whole value into a fractional box. This fixes the price example:

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

Let's go through the example line by line:

  1. count := 3 is a whole-number box holding 3.
  2. price := 2.5 is a fractional box holding 2.5.
  3. total := float64(count) * price: here float64(count) puts a copy of 3 into a fractional box, which gives 3.0. Both factors now have the type float64, and 3.0 * 2.5 gives 7.5.
  4. fmt.Println(total) prints 7.5.

You choose this direction when the expression involves a fractional number and the result's fractional part matters.

From fractional to whole

A whole-number box has no room for a fractional part. int(x) drops it instead of rounding. A negative number loses it the same way, so the result is closer to zero.

GO
x := 2.9
fmt.Println(int(x), int(-x)) // 2 -2
  1. int(x) cuts the fractional part off 2.9 and gives 2.
  2. int(-x) does the same with -2.9 and gives -2, not -3.

To round a positive number to the nearest whole one, add 0.5 before the conversion: int(x + 0.5) gives 3 for 2.9.

The trap of this direction is a loss halfway through. The expression int(price) * count also compiles but gives 6, not 7.5: the price loses its fraction before the multiplication.

Here the box picture stops working. A thing that does not fit into a box stays whole outside it. With int(x), the extra part of the number is gone for good, and Go gives no warning about it at run time.

Numbers written in code

Numbers written directly in the code behave differently from variables. Until such a number lands in a variable, it has no fixed type. Compare two cases:

  • fmt.Println(3 + 1.5) compiles and prints 4.5;
  • the variable a := 3 already has the type int, so it can be added to a float64 only after a conversion.

Common mistake

A fractional number written directly in the code is converted to int:

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

The compiler sees that the fractional part of such a number would be lost and refuses to build the program. Only a value in a variable can have its fractional part dropped, as in the int(x) example.

Key points

  • Go never converts types on its own: both values in an arithmetic operation have the same type.
  • A conversion is written like a call, float64(count), and the original variable does not change.
  • int(x) drops the fractional part instead of rounding: for x := 2.9 it gives 2.
  • A number written directly in the code has no fixed type, so 3 + 1.5 compiles.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way