Type Conversion

Go never converts between types behind your back. Adding an int to a float64, or an int32 to an int64, is a compile error until you convert one of them explicitly. This lesson shows the conversion syntax, what conversions do to values, and how numbers and strings are converted.

No automatic conversions

GO
package main

import "fmt"

func main() {
	count := 3
	price := 2.5
	var big int64 = 10
	fmt.Println(count*price, count+big)
}
Compiler error
./main.go:9:14: invalid operation: count * price (mismatched types int and float64)
./main.go:9:27: invalid operation: count + big (mismatched types int and int64)

This feels strict at first, but it removes a whole category of bugs where precision is lost or values overflow without anyone noticing.

Converting with T(value)

To convert, write the target type like a function call:

GO
package main

import "fmt"

func main() {
	count := 3
	price := 2.5
	var big int64 = 10

	fmt.Println(float64(count) * price)
	fmt.Println(int64(count) + big)

	total, n := 17, 5
	fmt.Println(total/n, float64(total)/float64(n))
}
Output
7.5
13
3 3.4

Converting total and n to float64 before dividing is the standard way to get a fractional average from whole numbers.

What conversions do to numbers

GO
package main

import "fmt"

func main() {
	price := 9.99
	fmt.Println(int(price), int(-price)) // drops the fraction (toward zero)

	big := 300
	fmt.Println(uint8(big)) // keeps the low 8 bits: 300 - 256

	neg := -1
	fmt.Println(uint32(neg)) // same bits, read as unsigned

	f := 16777217.0
	fmt.Println(float32(f)) // float32 can't hold every integer this large
}
Output
9 -9
44
4294967295
1.6777216e+07

Converting a float to an integer truncates; it doesn't round. (Converting the constant 9.99 directly, as in int(9.99), is a compile error instead — the compiler refuses to throw away the fraction of a constant.) Use int(math.Round(x)) if you want rounding. Converting to a smaller integer type keeps only the bits that fit, so check the range first if the value could be too large.

Untyped constants

A literal like 2 or 0.5 has no fixed type until it's used, so it fits wherever it makes sense without a conversion:

GO
package main

import (
	"fmt"
	"time"
)

func main() {
	price := 2.5
	fmt.Println(price * 2) // 2 becomes a float64 here

	var small int8 = 100
	fmt.Println(small + 20) // 20 becomes an int8

	fmt.Println(3 * time.Second) // 3 becomes a time.Duration
}
Output
5
120
3s

Lesson 10 explains untyped constants in more detail.

Numbers and strings

To turn a number into its decimal text, use the strconv package or fmt.Sprint — not string(n). Converting an integer with string() treats it as a Unicode code point, which is almost never what you want, so go vet reports it:

GO
package main

import "fmt"

func main() {
	n := 65
	fmt.Println(string(n))
}
go vet
main.go:7:14: conversion from int to string yields a string of one rune, not a string of digits
GO
package main

import (
	"fmt"
	"strconv"
)

func main() {
	n := 65
	fmt.Println(string(rune(n))) // if you really want the character: say so
	fmt.Println(strconv.Itoa(n) + " items")
	fmt.Println(fmt.Sprint(n, " items"))

	qty, err := strconv.Atoi("12")
	fmt.Println(qty*2, err)

	price, err := strconv.ParseFloat("19.95", 64)
	fmt.Println(price, err)

	_, err = strconv.Atoi("twelve")
	fmt.Println(err)
}
Output
A
65 items
65 items
24 <nil>
19.95 <nil>
strconv.Atoi: parsing "twelve": invalid syntax

Parsing can fail, so the parsing functions return a value and an error. Always check the error (lesson 44); <nil> means no error. Lesson 21 covers strconv fully.

Strings, bytes and runes

A string can be converted to a slice of bytes or a slice of runes (Unicode code points) and back. The conversions copy the data:

GO
package main

import "fmt"

func main() {
	s := "héllo"
	b := []byte(s)
	r := []rune(s)
	fmt.Println(len(b), b)
	fmt.Println(len(r), r)

	r[1] = 'e'
	fmt.Println(string(r), s)
}
Output
6 [104 195 169 108 108 111]
5 [104 233 108 108 111]
hello héllo

Conversions between named types

A type you define with type (lesson 36) is a distinct type, even when it has the same underlying type as another. Values move between them only by conversion:

GO
package main

import "fmt"

type Celsius float64
type Fahrenheit float64

func toF(c Celsius) Fahrenheit {
	return Fahrenheit(c*9/5 + 32)
}

func main() {
	temp := Celsius(21.5)
	fmt.Println(toF(temp))
	fmt.Println(float64(temp) + 1.5)
}
Output
70.7
23

Distinct types like these let the compiler stop you from mixing up Celsius and Fahrenheit, metres and feet, or user IDs and order IDs.

Try it yourself

Calculate the average of three test scores, rounded to one decimal place.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
85.33333333333333
85.3
Next: constants and iota.