>_
GolangStepByStep
Intern

Type Conversions

Explicit type casting in Go — no implicit coercion allowed

# 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 strconv for string↔number conversion in real-world code paths.
practice & review