Dev Skills

Type conversion

Why Go refuses to turn an int into a float for you.

Topic 7 of 98 · 5 min to read · 45 min to build
After this topic you can
  • Write explicit conversions between int, float64 and a named type.
  • Explain why string(rune(65)) gives "A" while strconv.Itoa(65) gives "65".
  • Predict the output of len on a string with non-English characters.

Go never converts a type for you

In most languages, an int next to a float is converted to a float for you. Go refuses. You write every conversion yourself.

This feels fussy until the first time it saves you. Implicit numeric conversion is where money code loses cents, and where an int64 is squeezed into an int32 with no warning. Go decided the cost of typing float64(a) is lower than the cost of finding that bug.

conversion.go
var a int = 10
var b float64 = float64(a) // required. "var b float64 = a" does not compile.
var c int64 = int64(a)
// Strings convert to and from byte and rune slices
s := "héllo"
bs := []byte(s) // the raw bytes
rs := []rune(s) // the unicode characters
fmt.Println(len(s), len(bs), len(rs)) // 6 6 5
`len` on a string counts BYTES, not characters, so "héllo" is 6 bytes but 5 runes. **Strings, bytes, and runes** covers why.

Numbers and text: strconv, not a cast

A cast like float64(a) changes one number type into another. Turning the number 42 into the text "42", or back, is a different job. That is what the strconv package is for, and the text side can fail, so it returns an error.

strconv.go
package main
import (
"fmt"
"strconv"
)
func main() {
s := strconv.Itoa(42) // "42"
n, err := strconv.Atoi("42") // 42, nil
fmt.Println(s, n, err)
f, err := strconv.ParseFloat("3.14", 64)
fmt.Println(f, err)
b, err := strconv.ParseBool("true")
fmt.Println(b, err)
fmt.Println(strconv.FormatInt(255, 16)) // "ff"
}
Watch out: string(65) gives you "A", not "65". It converts a code point to a character. To turn a number into its text form you need strconv.Itoa. go vet flags string(n) on an int for this reason, which is why code here writes string(rune(65)) when it really wants the character.

Try it yourself

press Run — this executes on the Go Playground
package main
import (
"fmt"
"strconv"
)
type Cents int64 // your own named type
func main() {
i := 7
f := float64(i) / 2 // without float64() this is integer division
fmt.Println("7/2 as ints :", 7/2)
fmt.Println("7/2 as floats:", f)
var c Cents = Cents(i) // a named type still needs the conversion
fmt.Println("cents:", c, "back to int:", int(c))
s := "héllo"
fmt.Println("bytes:", len(s), "runes:", len([]rune(s)))
// numbers <-> text needs strconv, not a cast
fmt.Println(strconv.Itoa(65), "vs", string(rune(65)))
n, err := strconv.Atoi("not a number")
fmt.Println("Atoi failed:", n, err)
}
Every conversion here is written out. This is the one place in the course that covers `strconv`, so keep it handy. Delete one and see what the compiler says.
The short version
  • Every conversion between types is written out by you. Go never widens silently.
  • float64(a), int64(a), []byte(s), []rune(s): all explicit.
  • len on a string counts bytes, not characters.
  • The cost of typing the conversion is lower than the cost of finding the bug it prevents.
Remember: Implicit numeric conversion is where money code loses cents. Go makes you write it down.
Build this before moving on
Convert an int to a float64 and back, and a string to both []byte and []rune. Print the length of each.
Then try these in your own editor
  1. 1Try var b float64 = someInt and read the compiler error.
  2. 2Convert a float64 with a fraction to an int and see what happens to the decimals.
  3. 3Take len() of a string with an emoji, then of []rune of the same string.
Check yourselfTake the Getting started test