Type Assertions and Reflection Boundaries
Overview
Type assertions extract concrete types from interfaces. Reflection provides runtime type inspection but should be used sparingly.
Type Assertions
var i interface{} = "hello"
s := i.(string) // Panic if wrong type
s, ok := i.(string) // Safe: ok is false if wrong type
if s, ok := i.(string); ok {
fmt.Println(s)
}Type Switches
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println("int:", v*2)
case string:
fmt.Println("string:", len(v))
case bool:
fmt.Println("bool:", !v)
default:
fmt.Printf("unknown: %T\n", v)
}
}The reflect Package
import "reflect"
v := 42
t := reflect.TypeOf(v) // int
val := reflect.ValueOf(v) // 42
fmt.Println(t.Name()) // "int"
fmt.Println(t.Kind()) // int
fmt.Println(val.Int()) // 42Struct Reflection
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
u := User{Name: "Alice", Age: 30}
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("%s: %s\n", field.Name, field.Tag.Get("json"))
}When to Use Reflection
✅ Good uses: - Serialization (JSON, XML) - ORM field mapping - Generic testing utilities - Dependency injection
❌ Avoid for: - Performance-critical code - Simple type checking - Normal business logic
Performance Cost
// Direct access: ~1ns
user.Name
// Reflection: ~100ns+
reflect.ValueOf(user).FieldByName("Name").String()Prefer Generics Over Reflection
// Go 1.18+: Use generics instead of reflect
func PrintSlice[T any](s []T) {
for _, v := range s {
fmt.Println(v)
}
}Summary
| Approach | Use Case |
|---|---|
| Type assertion | Extract known concrete type |
| Type switch | Handle multiple types |
| reflect | Runtime introspection (last resort) |
| Generics | Compile-time type safety |