Type Assert and Convert at Runtime
Type Assert and Convert at Runtime
Overview
Type assertions and conversions are not only compile-time checks. Failed asserts panic (or return ok=false); interface asserts consult dynamic type metadata in the interface word.
Diagram: assert path
interface value
│
├── i.(T) ok-form ──► value or ok=false
├── i.(T) bare ──► value or panic
└── type switch ──► branch on dynamic type
var i any = "x"
s := i.(string) // panic if wrong
s, ok := i.(string) // safe
switch v := i.(type) { case string: _ = v }Convert vs assert
Convert T(x) |
Assert i.(T) |
|
|---|---|---|
| Static types | both known | interface → concrete/interface |
| Failure | compile error | panic or ok=false |
| Cost | often pure codegen | type metadata compare |
Interface → interface
var r io.Reader = bytes.NewBuffer(nil)
rc, ok := r.(io.ReadCloser) // may failRuntime checks method set / itab compatibility for the pair of interface types.
Performance notes
- Hot asserts on varying types can miss predict and cost more than type switch with few cases
- Prefer static types at package boundaries when possible
any+ assert in loops is a smell (also escapes/allocs)
Experiment
go run .package main
import "fmt"
func main() {
var i any = 3
if _, ok := i.(string); ok {
fmt.Println("string")
} else {
fmt.Println("not string")
}
defer func() {
if r := recover(); r != nil {
fmt.Println("panic:", r)
}
}()
_ = i.(string)
}What to notice: comma-ok is quiet; bare assert panics.
Try next: Benchmark type switch vs chained asserts on a closed set of types.