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
- Convert numbers explicitly with T(v)
- Convert between string, []byte, and []rune
- Parse and format with strconv (Atoi, Itoa, ParseFloat)
- Assert an interface's concrete type with x.(T)
- Use the safe comma-ok assertion form
- Branch on type with a type switch
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)
- ❌ "mismatched types int and float64" — Go won't auto-convert. ✅ Convert explicitly: float64(i) + f.
- ❌ "panic: interface conversion" — a bare x.(T) failed. ✅ Use the comma-ok form v, ok := x.(T).
- ❌ string(myInt) gives a weird character — it's a code-point conversion. ✅ Use strconv.Itoa(myInt) for digits.
- ❌ Ignored the error from Atoi — bad input silently became 0. ✅ Check if err != nil.
🎯 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!
- ✅ Numeric conversions are always explicit: float64(i), int(x)
- ✅ Convert between string, []byte, and []rune
- ✅ Use strconv (Atoi/Itoa/ParseFloat) to parse text — and check the error
- ✅ Prefer the comma-ok assertion v, ok := x.(T) to avoid panics
- ✅ A type switch branches on the interface's dynamic type
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
- Previous: Formatting with fmt (Printf, Sprintf)
- Next: Struct Embedding & Composition — Promoted fields and methods, embedding interfaces, and composition over inheritance
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.