Type Conversions & Assertions

Reviewed & published by Brayan K

A conversion T(v) changes a value's type at compile time while a type assertion x.(T) extracts the concrete type stored inside an interface at run time — two distinct tools Go keeps explicit and safe.

Part of the free Go course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ Explicit Conversions: T(v)

Go performs no automatic numeric conversion — you must write it out: float64(i), int(x), uint8(i). Converting a float to an int truncates toward zero (no rounding). Conversions between string and []byte/[]rune are also built in. Watch the special case: string(65) gives the character "A", not the text "65".

package main

import "fmt"

func main() {
    // Go NEVER converts numeric types automatically. You must be explicit.
    var i int = 65
    var f float64 = float64(i) // int -> float64
    var u uint8 = uint8(i)     // int -> byte (uint8)
    fmt.Println(i, f, u)       // 65 65 65

    // Converting float to int TRUNCATES toward zero (no rounding).
    x := 9.99
    fmt.Println(int(x)) // 9

    // int <-> string conversions are special:
    // string(int) yields the CHARACTER for that code point.
    fmt.Println(string(rune(72))) // H, not "72"

    // string <-> []byte and string <-> []rune.
    bs := []byte("Go")
    fmt.Println(bs)         // [71 111]
    fmt.Println(string(bs)) // Go
}

2️⃣ strconv: Strings ↔ Numbers

To turn text into numbers (and back), use the strconv package — a plain conversion won't parse digits. Atoi reads a string into an int, Itoa does the reverse, and ParseFloat reads a float64. Crucially, these return an error on bad input rather than panicking — so you check err and handle it.

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // strconv converts BETWEEN strings and other types (parsing/formatting).
    n, err := strconv.Atoi("42") // string -> int
    fmt.Println(n, err)          // 42 <nil>

    s := strconv.Itoa(2024) // int -> string
    fmt.Println(s + "!")    // 2024!

    f, _ := strconv.ParseFloat("3.14", 64) // string -> float64
    fmt.Println(f * 2)                      // 6.28

    // Bad input returns an error instead of panicking.
    _, err2 := strconv.Atoi("oops")
    fmt.Println(err2) // strconv.Atoi: parsing "oops": invalid syntax
}

3️⃣ Type Assertions & Type Switches

When a value is held in an interface (like interface{}), you recover its real type with an assertion x.(T). The single-result form panics on a mismatch, so prefer the comma-ok form v, ok := x.(T), which sets ok to false instead. To handle several possibilities at once, a type switch (switch x := v.(type)) branches on the dynamic type cleanly.

package main

import "fmt"

func describe(v interface{}) string {
    // Type SWITCH: branch on the dynamic type held by an interface.
    switch x := v.(type) {
    case int:
        return fmt.Sprintf("int doubled: %d", x*2)
    case string:
        return fmt.Sprintf("string upper-len: %d", len(x))
    case bool:
        return fmt.Sprintf("bool negated: %t", !x)
    default:
        return fmt.Sprintf("unknown type %T", x)
    }
}

func main() {
    // Type ASSERTION with the comma-ok form (safe, no panic).
    var any interface{} = "hello"
    if s, ok := any.(string); ok {
        fmt.Println("it's a string of length", len(s))
    }
    if _, ok := any.(int); !ok {
        fmt.Println("not an int")
    }

    fmt.Println(describe(21))
    fmt.Println(describe("Gopher"))
    fmt.Println(describe(true))
    fmt.Println(describe(3.5))
}

🎯 Your Turn

Both numbers arrive as text. Fill in the blanks to parse them with strconv.Atoi, then add them.

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // Both numbers arrive as text. Parse them, then add.
    a, _ := strconv.___("18") // 👉 Atoi  -> 18
    b, _ := strconv.___("24") // 👉 Atoi  -> 24

    fmt.Println("sum:", a+b)

    // ✅ Expected output:  sum: 42
}

Common Errors (and the fix)

🎯 Mini-Challenge: Classify by Type

Write kind(v interface{}) string with a type switch that returns "int", "string", "bool", or "other", then loop over vals and print each value with its kind.

package main

import "fmt"

// 🎯 MINI-CHALLENGE: classify each value by its dynamic type.
// Write kind(v interface{}) string using a type switch that
// returns "int", "string", "bool", or "other". Then loop and print
// "<value> -> <kind>" for each item in vals.
//
// ✅ Expected output:
// 7 -> int
// hi -> string
// true -> bool
// 3.14 -> other

func main() {
    vals := []interface{}{7, "hi", true, 3.14}
    // your code here
}

🎉 Lesson Complete!

Practice quiz

How do you convert an int i to a float64?

  • i.(float64)
  • (float64)i
  • float64(i)
  • i as float64

Answer: float64(i). Go uses the target type as a function: float64(i).

Does Go convert numeric types automatically?

  • No, every numeric conversion is explicit
  • Yes, ints widen to floats
  • Yes, but only in arithmetic
  • Only for untyped constants in vars

Answer: No, every numeric conversion is explicit. Go has no implicit numeric conversion; you must write it out.

What does int(x) do to the float64 9.99?

  • Rounds to 10
  • Rounds to nearest even
  • Returns an error
  • Truncates toward zero to 9

Answer: Truncates toward zero to 9. Float-to-int truncates; use math.Round first if you need rounding.

What does string(rune(72)) produce?

  • "72"
  • "H"
  • 72
  • "r"

Answer: "H". Converting a code point to string yields that character; 72 is 'H'.

Which call turns the string "42" into the int 42?

  • strconv.Atoi("42")
  • int("42")
  • strconv.Itoa("42")
  • string(42)

Answer: strconv.Atoi("42"). Atoi parses a string to int; Itoa does int to string.

What does strconv.Itoa(2024) return?

  • The int 2024
  • The float 2024.0
  • The string "2024"
  • An error

Answer: The string "2024". Itoa converts an int to its decimal string.

What does strconv.Atoi do on bad input like "oops"?

  • Panics
  • Returns 0 and a non-nil error
  • Returns -1
  • Returns the empty string

Answer: Returns 0 and a non-nil error. strconv functions report an error instead of panicking.

Which is the safe (comma-ok) type assertion form?

  • v := x.(T)
  • v := x.(type)
  • v := assert(x, T)
  • v, ok := x.(T)

Answer: v, ok := x.(T). The comma-ok form sets ok to false on mismatch instead of panicking.

What does a bare x.(T) do if x does not hold type T?

  • Returns nil
  • Panics
  • Returns the zero value silently
  • Compile error

Answer: Panics. The single-result assertion panics on a type mismatch.

Which construct branches on the dynamic type of an interface value?

  • switch v { ... }
  • if v is T { ... }
  • switch x := v.(type) { ... }
  • typeof(v)

Answer: switch x := v.(type) { ... }. A type switch reads v.(type) and branches per concrete type.

Continue this course

Frequently asked questions

Why does Go make me convert numeric types explicitly?

Go has no implicit numeric conversions on purpose — it prevents silent precision loss and surprising overflow. If you add an int and a float64, the compiler stops you until you convert one with float64(x) or int(y). That extra keystroke makes the cost of the conversion visible in the code, which is a core Go design value.

What is the difference between a conversion and an assertion?

A conversion T(v) changes a value from one known type to another at compile time, like float64(i) or []byte(s). A type assertion x.(T) is a run-time operation on an interface value: it checks whether the interface currently holds type T and gives you that concrete value. Conversions work on concrete types; assertions only work on interface types.

Why does string(65) give "A" instead of "65"?

Converting an integer to a string interprets the number as a Unicode code point and produces that single character — 65 is 'A'. It does NOT produce the digit text. To turn the number 65 into the string "65" use strconv.Itoa(65) (or fmt.Sprintf("%d", 65)). Go vet will warn you about the int-to-string conversion for this reason.

When should I use the comma-ok form of a type assertion?

Always use the two-result form, value, ok := x.(T), unless you are absolutely certain of the type. The single-result form x.(T) panics if the assertion fails, while the comma-ok form sets ok to false and gives you a zero value, so you can handle the mismatch gracefully instead of crashing.

What does strconv.Atoi return if the string isn't a number?

It returns the zero value (0) plus a non-nil error describing the problem, such as 'strconv.Atoi: parsing "oops": invalid syntax'. strconv functions never panic on bad input — they report an error you should check. That's why you'll usually see n, err := strconv.Atoi(s) followed by an if err != nil check.