reflect lets a program examine types it was not written against: the fields of a struct, the kind of a value, the tag on a field. It is what encoding/json is built from.
Examples
Every value has a type, and every type falls into one of a fixed set of kinds. Kind is the question worth asking, because a named type like type ID int still reports Int.
for _, v := range []any{42, "go", 3.5, []int{1}, map[string]int{}, User{}} {
t := reflect.TypeOf(v)
fmt.Println(t, "->", t.Kind())
}int -> int
string -> string
float64 -> float64
[]int -> slice
map[string]int -> map
main.User -> structWalking a struct's fields is what every encoder in the standard library does. The tag is how it learns the name to write.
type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
age int
}
t := reflect.TypeOf(User{})
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Printf("%s %s tag=%q exported=%v\n", f.Name, f.Type, f.Tag.Get("json"), f.IsExported())
}Name string tag="name" exported=true
Email string tag="email,omitempty" exported=true
age int tag="" exported=falseReflection can change a value, but only if it reached it through a pointer. Pass the value itself and you are holding a copy.
u := User{Name: "Ada"}
v := reflect.ValueOf(&u).Elem()
v.FieldByName("Email").SetString("ada@example.com")
fmt.Println(u.Name, u.Email)Ada ada@example.comTwo maps cannot be compared with ==, and neither can two slices. DeepEqual walks them instead.
a := map[string][]int{"x": {1, 2}}
b := map[string][]int{"x": {1, 2}}
fmt.Println(reflect.DeepEqual(a, b))true| Function | Description |
|---|---|
TypeOf(i any) Type reflect.TypeOf(42).Kind() // reflect.Int | The dynamic type of a value, as something you can ask questions about. |
| |
ValueOf(i any) Value reflect.ValueOf("go").Len() // 2 | The value itself, wrapped so it can be read and sometimes written. |
Reflection can only write through a pointer, and the reason is ordinary Go rather than anything reflective. | |
Value.Elem() Value | What a pointer or interface points at. |
Reflection can only write through a pointer, and the reason is ordinary Go rather than anything reflective. | |
Type.Kind() Kind t.Kind() == reflect.Slice | Which of Go's built-in categories the type falls into. |
| |
Type.Name() string t.Name() // "User" | The declared name, empty for an unnamed type like []int. |
Type.NumField() int t.NumField() // 3 | How many fields a struct type has. Panics on anything else. |
Type.Field(i int) StructField t.Field(0).Name // "Name" | One field's name, type, tag, and whether it is exported. |
| |
Type.NumMethod() int t.NumMethod() | How many exported methods the type has. |
Type.Implements(u Type) bool t.Implements(errType) | Whether the type satisfies an interface. |
| |
Value.Interface() any v.Interface().(string) | The value back as an ordinary any, ready to type-assert. |
Value.FieldByName(name string) Value v.FieldByName("Email") | One struct field by name, zero Value if there is no such field. |
Value.CanSet() bool v.Field(0).CanSet() // false on a copy | Whether writing to this value would work. |
| |
Value.SetString(s string) v.FieldByName("Name").SetString("Ada") | Writes a string. Panics unless CanSet is true and the kind matches. |
Value.SetInt(x int64) v.SetInt(7) | Writes any signed integer kind. |
Value.Set(x Value) v.Set(reflect.ValueOf(other)) | Writes another Value, which must be assignable to this one. |
Value.IsZero() bool v.IsZero() | Whether the value is its type's zero value. |
Value.IsNil() bool v.IsNil() | Whether a pointer, slice, map, channel, func or interface is nil. |
| |
DeepEqual(x, y any) bool reflect.DeepEqual(a, b) // true | Compares two values recursively, including maps and slices. |
| |
Zero(t Type) Value reflect.Zero(t) | A new zero value of the given type. |
New(t Type) Value reflect.New(t).Elem() | A pointer to a new zero value, the reflective form of new(T). |
| |
MakeSlice(t Type, len, cap int) Value reflect.MakeSlice(t, 0, 4) | A new slice of the given type, length and capacity. |
Kind | The category of a type: Int, String, Slice, Map, Struct, Ptr, Func, Chan, Interface, and the rest. |
StructField | One field of a struct type: Name, Type, Tag, Index, and Anonymous for an embedded field. |
StructTag f.Tag.Get("json") // "name,omitempty" | The string in backticks after a field. Get returns one key's value, Lookup reports whether it was there. |
Related
Reflection moves errors from compile time to run time. A misspelled field name is a panic in production rather than a message from the compiler, and the code is slower and harder to follow.
Most of what people reach for it can be done with a type assertion, a type switch, or an interface. It earns its place when the types genuinely are not known while you are writing the code: a decoder, a test helper comparing arbitrary values, an ORM mapping rows onto structs.