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,float64and a named type. - Explain why
string(rune(65))gives "A" whilestrconv.Itoa(65)gives "65". - Predict the output of
lenon 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 = 10var 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 slicess := "héllo"bs := []byte(s) // the raw bytesrs := []rune(s) // the unicode charactersfmt.Println(len(s), len(bs), len(rs)) // 6 6 5Numbers 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 mainimport ( "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 mainimport ( "fmt" "strconv")type Cents int64 // your own named typefunc 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)}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.lenon 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
- 1Try
var b float64 = someIntand read the compiler error. - 2Convert a float64 with a fraction to an int and see what happens to the decimals.
- 3Take
len()of a string with an emoji, then of[]runeof the same string.
Official documentation