# Why Type Conversions Matter
Go uses explicit type conversions. It never silently converts values for you. If two values have different types, you must write the conversion yourself using T(x) syntax.
This design avoids hidden bugs: precision loss, accidental integer truncation, and confusing coercion rules. In Go, every conversion is visible in code review.
Real-World Analogyclick to expand
Think of conversions like moving liquids between containers with different capacities. Pouring from a 2L bottle (int64) into a tiny cup (int8) can spill. Go forces you to do that pour explicitly so you can decide if data loss is okay.
In production systems, this matters in billing, analytics, and APIs where data types cross boundaries constantly (JSON strings ↔ numeric fields, DB types ↔ Go structs, service contracts ↔ internal models).
# Basic Conversion Syntax
The only conversion form in Go is T(x), where T is the destination type.
package main
import "fmt"
func main() {
var i int = 42
f := float64(i) // int -> float64
u := uint(f) // float64 -> uint (fraction discarded)
fmt.Println(i, f, u)
}# Breaking It Down Step by Step
float64(i)Widening from int to float64 usually preserves value for small integers, but very large integers may lose precision because floating-point has finite mantissa bits.
int(3.9)Float-to-int conversion truncates toward zero. So int(3.9) == 3 and int(-3.9) == -3. It does not round.
int8(300)Narrowing can overflow silently. int8 range is -128 to 127, so converting 300 wraps and gives an unexpected value. Always check bounds before narrowing.
# Numeric Pitfalls and Safe Patterns
package main
import (
"errors"
"fmt"
"math"
)
func SafeToInt32(n int64) (int32, error) {
if n > math.MaxInt32 || n < math.MinInt32 {
return 0, errors.New("overflow: int64 does not fit in int32")
}
return int32(n), nil
}
func main() {
a, err := SafeToInt32(100)
fmt.Println(a, err) // 100 <nil>
b, err := SafeToInt32(math.MaxInt64)
fmt.Println(b, err) // 0 overflow error
}# Strings, Bytes, Runes, and strconv
Text conversions are a frequent source of bugs. Remember these rules:
[]byte(s)copies string bytes into a mutable byte slice.[]rune(s)decodes Unicode code points.string(65)means Unicode code point U+0041, i.e. "A".- Use
strconv.Itoa(65)to get decimal text "65".
package main
import (
"fmt"
"strconv"
)
func main() {
s := "héllo"
bytes := []byte(s)
runes := []rune(s)
fmt.Println("byte length:", len(bytes)) // UTF-8 bytes
fmt.Println("rune length:", len(runes)) // Unicode chars
n := 65
fmt.Println(string(n)) // A
fmt.Println(strconv.Itoa(n)) // 65
}⚡ Key Takeaways
- Go never performs implicit coercion; use
T(x)for every conversion. - Float-to-int truncates toward zero; narrowing conversions can overflow silently.
- Use bounds checks before converting from larger numeric types to smaller ones.
string(65)is a Unicode character, not decimal text.- Use
strconvfor string↔number conversion in real-world code paths.