skip to content

Dynamic Types at Runtime

The reflect package inspects and rewrites values whose types were unknown at compile time, through TypeOf, ValueOf, Kind and struct tags. It is the machinery under every encoder.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

explore

questions

29

What do reflect.TypeOf and reflect.ValueOf return, and how do you get an ordinary Go value back?

level: juniorimportance: must knowfreq 48%

answer

  1. two entry points, two different nouns
  2. one describes shape, one holds data
  3. both parameters are declared any
  4. Interface() is the door back out
  5. then a type assertion names the type

basics

~20 s

reflect.TypeOf returns a reflect.Type describing the dynamic type of whatever you pass it; reflect.ValueOf returns a reflect.Value wrapping the data itself. Value.Interface() hands the data back as an any, which you then type-assert to a concrete type.

solid answer

~40 s

Both take a parameter of type `any`, so the concrete value you pass is boxed into an interface first, and reflection reads the dynamic type and data out of that interface. `reflect.TypeOf(x)` gives you a `reflect.Type` — the shape: its `Kind`, its declared `Name`, its methods and fields. `reflect.ValueOf(x)` gives you a `reflect.Value` — a handle on the data, with typed accessors like `Int()`, `Float()` and `Len()` whose legality depends on the value's `Kind`. The trip back out is `Value.Interface()`, which returns an `any` you type-assert: `n := v.Interface().(int)`. Two consequences worth saying out loud: the value is copied on the way in, and `Interface()` panics if the `reflect.Value` was obtained by reading an unexported struct field, which `CanInterface()` reports up front.

code

go · 10 lines
go
var x float64 = 3.4

t := reflect.TypeOf(x)
v := reflect.ValueOf(x)

fmt.Println(t.Kind()) // float64
fmt.Println(v.Kind()) // float64

back := v.Interface().(float64)
fmt.Println(back) // 3.4

go deeper

for a junior

Be ready to name both entry points and say which one describes the type and which one holds the data, then show the way back with Interface() plus a type assertion.

for a middle

Explain that both parameters are declared any, so the argument is boxed and reflection reads the dynamic type out of the interface, and that the value is copied on the way in.

for a senior

Show the guards a real walker needs: check Kind before calling a typed accessor, and check CanInterface before Interface, so a value read from an unexported field does not panic your inspector.

for a principal

Be able to argue when reflection is the right tool at all, versus type parameters or generated code, and what a reflective boundary costs a team in readability and in run-time overhead.

## Why the package exists Go is statically typed: normally the compiler knows the type of every expression, and code that wants to branch on a type does it with a type switch or a type assertion, both of which require you to *name* the types at compile time. Reflection is for the cases where you cannot: a JSON encoder, a struct-to-row mapper, a table-driven test helper, an interactive inspector that prints whatever value the session just evaluated. Those need to ask, at run time, "what am I holding, and what is inside it?" The `reflect` package answers that with two objects and two entry points. ## reflect.TypeOf — the shape `reflect.TypeOf(i any) reflect.Type` returns a description of the *dynamic* type of the value inside the interface you passed. `reflect.Type` is an interface with methods such as `Kind()` (which of the roughly two dozen built-in categories this is: `Int`, `Slice`, `Struct`, `Pointer`, `Map`, …), `Name()` (the declared name, empty for unnamed composite types), `String()` (a readable package-qualified form), plus the structural methods — `NumField`, `Elem`, `NumMethod` and friends. A `reflect.Type` value is canonical: the runtime hands out one descriptor per type, so two `reflect.Type` values are `==` exactly when they describe the same type. That makes `reflect.TypeOf(x) == reflect.TypeOf(time.Time{})` a legitimate identity test. ## reflect.ValueOf — the data `reflect.ValueOf(i any) reflect.Value` returns a handle on the data itself. A `reflect.Value` is a struct (not an interface), and it carries three things internally: which type it is, where the data is, and a set of flags. From it you can ask `Kind()`, `Type()`, `Len()`, `Index(i)`, `MapKeys()`, `Int()`, `Float()`, `Bool()` and so on — but each typed accessor is only legal for the matching `Kind`, and calling the wrong one panics. `Kind()` is therefore the gate you check before you read. Note that the value is *copied* on the way in. Passing `x` to `ValueOf` boxes a copy into the `any` parameter, so the `reflect.Value` you get back refers to that copy, not to your variable. ## The round trip back `Value.Interface() any` is the door out of reflection and back into ordinary Go: ```go v := reflect.ValueOf(3.4) back := v.Interface().(float64) // 3.4 ``` The assertion is required because `Interface()` is typed `any` — reflection can tell you at run time that the dynamic type is `float64`, but the compiler still needs you to state the static type you want. If you get it wrong, the assertion panics (or returns `ok == false` in the two-result form). One restriction: `Interface()` panics with "cannot return value obtained from unexported field or method" when the `reflect.Value` was read out of an unexported struct field. Reflection is allowed to *look at* such fields, but handing their contents back to normal code would let any package sidestep another package's encapsulation, so the value is flagged read-only. `CanInterface()` tells you in advance whether the call is safe. ## How the pieces line up For any value `x`, all of the following hold: - `reflect.TypeOf(x)` and `reflect.ValueOf(x).Type()` describe the same type. - `reflect.ValueOf(x).Kind()` and `reflect.TypeOf(x).Kind()` are the same `Kind`. - `reflect.ValueOf(x).Interface()` restores an `any` equal to `x`. So `Type` is the noun that answers "what shape", `Value` is the noun that answers "what data", and `Kind` is the coarse category both of them expose. ## Practical shape of inspector code Most reflection code is a small loop of the same three moves: get a `Value`, switch on its `Kind` to decide how to read it, and either recurse into its parts or call `Interface()` to hand a leaf back to ordinary code. Everything else in the package is a variation on that. And because each of those steps is a run-time lookup rather than a compiled-in offset, reflective code is measurably slower than the equivalent statically typed code — worth remembering before you reach for it on a hot path. ## What to say in an interview Name both entry points, say that both take `any` so the argument is boxed first, say that `Type` is the shape and `Value` is the data, and finish with `Interface()` plus a type assertion as the way back. Mentioning that the value is copied, and that `Interface()` refuses values read from unexported fields, is what separates a memorised answer from a used-it answer.

  • Why are the parameters of reflect.TypeOf and reflect.ValueOf declared as any rather than a type parameter?
    Because reflection deliberately works on values whose type is unknown at compile time. Declaring `any` boxes whatever you pass into an interface, and `reflect` then reads the dynamic type and data out of that interface. A side effect is that a `reflect.Value` from `ValueOf` never reports `Kind` `Interface` — the interface wrapper is unwrapped on the way in, and you see the concrete type it held.
  • When does Value.Interface() panic?
    When the `reflect.Value` was obtained by reading an unexported struct field. Such values carry a read-only flag, because handing their contents out as an `any` would let any package bypass another package's encapsulation. `CanInterface()` reports whether the call is safe, so a general-purpose walker checks it before calling.
  • How is this different from a plain type assertion like x.(float64)?
    A type assertion requires you to name the type in source, so it only works for types you knew about when you wrote the code, and it is far cheaper. Reflection works when the type is not known until run time — a generic encoder, a mapper, an inspector — at the cost of run-time lookups instead of compiled-in offsets.

reflect.Type is the blueprint and reflect.Value is the object built from it. Interface() is the door out of the workshop and back into ordinary Go, where you still have to say aloud which type you are carrying.

saying these in an interview costs you the question

  • Says reflect.ValueOf returns the value's memory address
  • Claims Value.Interface() returns a string
  • Uses a reflect.Value directly in arithmetic
  • Confuses reflect.Type with the Kind category
  • Thinks reflection mutates the original variable by default
open as a page

What does reflect.New(t) return in Go, and why do you call Elem() on the result?

level: juniorimportance: must knowfreq 34%

basics

~20 s

reflect.New(t) allocates a zero value of type t and returns a reflect.Value holding a pointer to it, exactly like new(T). Elem() follows that pointer to the value itself, which lives in addressable memory and can be filled in.

open as a page

Why does reflect.DeepEqual report a nil []string and an empty []string{} as unequal?

level: juniorimportance: must knowfreq 55%

basics

~20 s

reflect.DeepEqual requires two slices to be both nil or both non-nil before it compares length and elements. A nil slice and an empty slice fail that first check, so it reports false even though both have length zero.

open as a page

Why does writing a field via reflect.ValueOf(cfg) panic when reflect.ValueOf(&cfg).Elem() works?

level: juniorimportance: must knowfreq 55%

basics

~20 s

reflect.ValueOf copies its argument into an interface, so the struct it holds has no address and every Set call panics. Passing &cfg and then calling Elem aims reflect at the original variable, which is addressable and therefore settable.

open as a page

Using Go's reflect package, how do you list a struct's fields and read each field's struct tag?

level: juniorimportance: must knowfreq 50%

basics

~10 s

reflect.TypeOf gives you the struct's Type; Type.NumField reports how many fields it declares and Type.Field(i) returns a StructField holding that field's Name, Type and Tag. Tag.Get reads one tag key out of it.

open as a page

Why is a reflect-based struct mapper slower than hand-written field assignment, and where does the allocation come from?

level: middleimportance: must knowfreq 48%

basics

~20 s

Reflection moves work from compile time to run time. Every call boxes values into interfaces, re-inspects the type, and reaches fields through calls that cannot inline. The boxing is what allocates, once per value converted.

open as a page

In Go's reflect package, how do you invoke a method chosen by its name at runtime?

level: juniorimportance: should knowfreq 38%

basics

~10 s

Wrap the value with reflect.ValueOf, look the method up with Value.MethodByName, then invoke it with Value.Call, passing arguments as a []reflect.Value. Call hands back the results as []reflect.Value, which you unwrap with Value.Interface.

open as a page

In Go's reflect package, how does Value.Kind differ from Type.Name, and which should you switch on?

level: middleimportance: should knowfreq 40%

basics

~20 s

Kind is one of a fixed set of built-in categories describing a value's underlying representation, such as Int, Slice or Struct. Name is the declared name of a named type and is empty for unnamed types. Structural code switches on Kind.

open as a page

Why does reflect.Value.Call panic on an argument mismatch, and how do you validate a call first?

level: middleimportance: should knowfreq 33%

basics

~20 s

Nothing about a reflected call is checked at compile time, so reflect.Value.Call reports a wrong argument count or type the only way it can, by panicking. Validate first with the method value's Type: NumIn, In(i) and AssignableTo.

open as a page

Why does reflect.New(mapType).Elem() give an unusable map in Go, and what should you use?

level: middleimportance: should knowfreq 38%

basics

~20 s

The zero value of a map type is a nil map, and reflect.New only ever gives you the zero value, so writing an entry panics. Use reflect.MakeMap for maps, reflect.MakeSlice for slices and reflect.MakeChan for channels.

open as a page

How do you cache a reflect-built field plan keyed by reflect.Type, and what does that stop repeating?

level: middleimportance: should knowfreq 35%

basics

~20 s

Walk each destination type once, store the resulting field-index plan in a map keyed by its reflect.Type, and look the plan up per row. reflect.Type values are canonical and comparable, so they work directly as map keys.

open as a page

Why can reflect.DeepEqual(x, x) return false for some values of x?

level: middleimportance: should knowfreq 38%

basics

~20 s

reflect.DeepEqual is not reflexive. A float NaN anywhere in the value fails the == that DeepEqual falls back to for numbers, and a non-nil func value is deeply equal to nothing at all, itself included.

open as a page

Why does reflect.Value.CanSet return false for an addressable but unexported struct field?

level: middleimportance: should knowfreq 40%

basics

~20 s

Because reflect refuses to let outside code break a package's encapsulation. A Value reached through an unexported field is flagged read-only: CanAddr stays true, CanSet is false, and both Set and Interface on it panic.

open as a page

When you walk a struct with reflect, how do embedded fields appear and how do you reach their promoted fields?

level: middleimportance: should knowfreq 34%

basics

~10 s

An embedded struct counts as one field: Type.Field(i) returns it with Anonymous true and Name equal to the embedded type's name. Its inner fields are not in NumField, so recurse or call reflect.VisibleFields.

open as a page

What does StructField.Tag.Get return when a struct tag is malformed, and how does Tag.Lookup differ?

level: middleimportance: should knowfreq 42%

basics

~20 s

Get returns the empty string in three different situations: the key is absent, its value is genuinely empty, or the tag's syntax broke so parsing stopped early. Lookup returns the value plus an ok flag, which separates absent from present-but-empty.

open as a page

Why does reflect.TypeOf return nil for a nil interface value, and how do you stop an inspector panicking on it?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A nil interface value carries no dynamic type, so there is nothing for reflect.TypeOf to describe and it returns a nil reflect.Type; any method call on that nil panics. reflect.ValueOf returns the zero Value instead, so guard with Value.IsValid before touching it.

open as a page

Why does a reachable reflect.Value.MethodByName stop Go's linker from pruning methods?

level: seniorimportance: should knowfreq 27%

basics

~20 s

The name is a run-time string, so the linker cannot prove which method it selects. Once MethodByName is reachable it conservatively keeps exported methods, so the binary grows and every exported method stays callable from data.

open as a page

A builder calls reflect.MakeSlice(t, n, n), then reflect.Append(s, v) in a loop, yet the result holds n zero values and none of the appended ones. What are the two bugs?

level: seniorimportance: should knowfreq 34%

basics

~20 s

reflect.MakeSlice takes length second and capacity third, so MakeSlice(t, n, n) starts with n zero elements; it should be MakeSlice(t, 0, n). And reflect.Append returns a new Value, so its result must be assigned back.

open as a page

Where do type parameters actually replace reflection in a mapping layer, and where can they not?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Type parameters replace reflection wherever code is generic over a whole value: containers, algorithms, typed caches, all without interface boxing. They cannot enumerate an arbitrary struct's fields, so field-by-field mapping still needs reflection or generated code.

open as a page

reflect.DeepEqual compares unexported fields too — how can that make a golden comparison fail only in CI?

level: seniorimportance: should knowfreq 40%

basics

~20 s

reflect.DeepEqual compares every field, exported and unexported, including state you never set: a time.Time's monotonic clock reading and location pointer, or a lazily filled cache. When the CI machine's zone, clock or environment differs, those hidden fields differ and the comparison fails.

open as a page

A reflect-based config loader leaves every setting at its default — how do you diagnose it?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Print CanAddr and CanSet for each Value the walk touches. Both false at the top level means the loader was handed a struct copy instead of a pointer, and its defensive skip-if-not-settable guard turned that into silence. Validate the pointer at the entry point.

open as a page

Why does reflect.Value.String() on a Value holding an int return a placeholder instead of panicking?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

String is the one typed accessor that never panics on the wrong Kind, because reflect.Value must satisfy the Stringer convention and be printable. On a non-string value it returns a form like <int Value>. Int, Float and Bool panic instead.

open as a page

What does reflect.MakeFunc create, and when would you reach for it in Go?

level: middleimportance: nice to knowfreq 21%

basics

~20 s

reflect.MakeFunc builds a function value of a func type you choose at runtime, implemented by a callback that receives arguments as []reflect.Value and returns results the same way. Ordinary typed code can then call it like any other function.

open as a page

In Go's reflect package, how does reflect.Zero(t) differ from Value.IsZero()?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

reflect.Zero(t) constructs a value: the zero value of type t, explicitly not addressable and not settable. Value.IsZero() inspects a value and reports whether it already equals the zero value of its own type. One builds, one asks.

open as a page

How does reflect.DeepEqual terminate on a cyclic structure instead of recursing forever?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

reflect.DeepEqual records each pair of addresses it is already comparing and returns true when it meets the same pair again, so a cycle terminates instead of recursing forever. Identical pointers, map objects and slice backing arrays short-circuit the same way.

open as a page

Why is reflect.Value.Elem settable for a pointer but not for a value held in an interface?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

Elem on a pointer returns the storage the pointer names, so it is addressable and settable. Elem on an interface returns a copy of the dynamic value the interface currently holds, which has no address of its own, so CanSet is false.

open as a page

A reflect-based column mapper silently drops struct fields - how do you tell an unexported field from a missing tag?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

Check StructField.IsExported, or equivalently whether PkgPath is empty: a non-empty PkgPath means the field name is unexported. A missing tag is a separate condition, reported by Tag.Lookup returning ok false, and the two deserve different handling.

open as a page

What would you require before approving reflect.MethodByName dispatch of admin commands in a Go service?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Treat it as opening an API, not adding a feature. Require an allowlist of command names on one handler type, per-command authorization, validation before any call, and a test resolving every registered name so a rename cannot break dispatch silently.

open as a page

Your reflect-based row mapper is the hot path of a nightly ten-million-row batch: keep it, generate mappers, or go generic?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Decide on measured cost weighed against the obligations you are creating for everyone else. A cached reflective plan is usually enough. Generated mappers buy speed and build-time breakage, and cost a generation step CI must police forever.

open as a page