Free — no signup required

Reflection: Inspecting the Unknown

2 min read

Runtime Type Introspection

In a statically typed language like Go, variables have a fixed type known at compile time. The compiler uses this information to generate efficient machine code — it knows exactly how many bytes a User struct occupies and where each field lives in memory.

But what if you are writing a function like json.Marshal? It needs to accept any struct, inspect its fields, read their values, and examine struct tags — all without knowing the struct definition at compile time. This is the problem reflection solves.

Reflection (the reflect package) allows a running program to examine its own structure at runtime. There are two core types:

  1. reflect.Type: Describes what something is — its kind (struct, slice, map, int), its name, its fields, and their tags. Think of it as the blueprint.
  2. reflect.Value: Describes what is inside something — the actual data stored in a variable at this moment. Think of it as the contents.
package main

import (
    "fmt"
    "reflect"
)

type Server struct {
    Host string
    Port int
}

func inspect(x interface{}) {
    t := reflect.TypeOf(x)
    v := reflect.ValueOf(x)

    fmt.Printf("Type name: %s\n", t.Name())   // "Server"
    fmt.Printf("Kind: %s\n", t.Kind())         // "struct"
    fmt.Printf("Num fields: %d\n", t.NumField())

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)
        fmt.Printf("  Field %s = %v\n", field.Name, value)
    }
}

func main() {
    s := Server{Host: "localhost", Port: 8080}
    inspect(s)
}

Expected output:

Type name: Server
Kind: struct
Num fields: 2
  Field Host = localhost
  Field Port = 8080

The interface{} parameter (or any in Go 1.18+) is the entry point for reflection. Every value in Go can be assigned to an interface{}, and from there, reflect.TypeOf and reflect.ValueOf can unwrap it. This is why almost every reflection-based function accepts interface{} as its argument — it is the universal container that lets reflection get started.

Interview Tip

Interviewers sometimes ask: "How does encoding/json handle arbitrary structs?" The answer is reflection. json.Marshal calls reflect.TypeOf to discover field names and struct tags (like `json:"host"`), then calls reflect.ValueOf to read the actual field values. A strong follow-up is to mention that the standard library caches the reflected type information to avoid re-doing the expensive lookup on every call — this is why sync.Map or similar caching structures appear in serialization library internals.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free