Type Assertions
Interface values at runtime
Section titled “Interface values at runtime”When a value is stored in an interface variable, Go keeps two things under the hood: a pointer to the concrete type descriptor and a pointer to the concrete value. A type assertion extracts the concrete value back out.
The single-value form
Section titled “The single-value form”var s Shape = Circle{Radius: 5}c := s.(Circle) // asserts that s holds a Circlefmt.Println(c.Radius)If s does not hold a Circle, this panics at runtime. Use this form only when you are certain of the dynamic type — for example, right after a type check.
The comma-ok form
Section titled “The comma-ok form”The safe variant returns a boolean instead of panicking:
c, ok := s.(Circle)if ok { fmt.Println("it's a circle with radius", c.Radius)} else { fmt.Println("not a circle")}ok is true when the assertion succeeds, false otherwise. c is the zero value of Circle when ok is false. Always prefer the comma-ok form when the dynamic type is uncertain.
The type switch
Section titled “The type switch”When you need to branch on several possible types, a type switch is cleaner than chained if ok blocks:
switch v := x.(type) {case Circle: fmt.Println("circle", v.Radius)case Rect: fmt.Println("rect", v.W, v.H)default: fmt.Printf("unknown: %T\n", v)}Inside each case, v is already the concrete type — no additional assertion needed. The default case fires when none of the listed types match.
Asserting to an interface
Section titled “Asserting to an interface”You can also assert from one interface to another:
var w io.Writer = os.Stdoutif rc, ok := w.(io.ReadWriter); ok { // os.Stdout implements io.ReadWriter _ = rc}This is how standard library code checks for optional capabilities (e.g., whether a Writer also supports Seek).
package main
import "fmt"
type Animal interface { Sound() string}
type Dog struct{ Name string }type Cat struct{ Name string }
func (d Dog) Sound() string { return "Woof" }func (c Cat) Sound() string { return "Meow" }
func identify(a Animal) { // comma-ok form — safe if d, ok := a.(Dog); ok { fmt.Printf("Dog named %s says %s\n", d.Name, d.Sound()) return } // type switch for multiple cases switch v := a.(type) { case Cat: fmt.Printf("Cat named %s says %s\n", v.Name, v.Sound()) default: fmt.Printf("Unknown animal: %T\n", v) }}
func main() { animals := []Animal{Dog{Name: "Rex"}, Cat{Name: "Luna"}} for _, a := range animals { identify(a) }}Loading Go runtime (first run only, ~8 MB)…