This is a quick explanation of the reflect package in Go, originally written for some of my coworkers, because I find the official introduction a bit convoluted. This post assumes that you already know the basics of Go, including the usage of interface{}, and understand why you might want reflection. And a reminder: anything you can do without reflect is probably better done that way; and anything you can’t might be better not done at all.

Values and Types

There are two main types in reflect: Value and Type. These are fairly self-explanatory: they’re reflect’s representation of Go’s values and types, and we can create them by calling reflect.ValueOf and reflect.TypeOf on any Go value. Their methods are the bulk of the reflect package. For example:

s := "I'm a string!"
sValue := reflect.ValueOf(s)
sValue.Len() // 13

stringType := reflect.TypeOf(s) // or sValue.Type()
stringType.Name() // "string"

type myStruct struct{
foo int
bar bool
}

reflect.ValueOf(myStruct{123, false}).Field(0) // reflect.ValueOf(123)


Most of the reflect package is methods on Type and Value: for example for a map type one can get the key and value types; and for a map value one can ask for the keys as a slice of reflect.Value. (The documentation, of course, has the details.)

To convert a reflect.Value back to an ordinary Go value, one uses value.Interface(); this returns an interface{} which the caller likely must cast to the expected type. (reflect provides helpers like value.String() to do this for common types.)

Kinds

The last important type in reflect is Kind, which is used to tell which form of builtin a particular type is. (It’s unrelated to the type-theoretic notion of “kind”.) For example, a Kind tells us if we have a map or a func or an int, but not what the keys and values of the map are. It’s probably best explained by listing the enum values:

    Invalid
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer


Every reflect.Type fits into one of these, which we can check by type.Kind() or value.Kind().

Note that almost everything in reflect will panic if given invalid data. For example, reflect.Value(123).Field(0) (getting a field on an int) will panic.

What can it do?

In general, reflect is designed so that you can with reflect exactly that which you can do in ordinary Go, just more generally. (There are a few exceptions, like defining methods.) For example, it can:

• index into a slice or map (of arbitrary element type) using reflect.Value.Index or reflect.Value.MapIndex

• call a function (of arbitrary signature) using reflect.Value.Call

• create a channel (of arbitrary element type) using reflect.MakeChan

Values which can be set in ordinary Go can also be set with reflect; for example:

s := []int{1, 2, 3}
v := reflect.ValueOf(s)
v.Index(1).Set(reflect.ValueOf(4))  // sets s[1] = 4


But values which are normally unsettable still are with reflect:

v := reflect.ValueOf(3)
v.Set(reflect.ValueOf(4))  // panics: can't set a literal


So you can’t use reflect to escape ordinary memory safety guarantees, for example, and access to unexported struct fields is limited.

Interfaces

Interface types in reflect are a bit weird. Basically, for most purposes, reflect never looks at the interface, only at the underlying value, because all its constructors accept interface{}. For example:

type Stringer interface{ String() string }
type Foo struct{}
func (foo Foo) String() string { return "it's a foo!" }

var v Stringer
v = Foo{}
reflect.TypeOf(v) // Foo, not Stringer


It’s possible to get interface values in reflect, such as by looking at the element type of a []Stringer, but it’s uncommon.

That’s it!

I hope this makes reflect seem a bit less scary. While the code you write with it may be very abstract, reflect itself isn’t too complicated. Use with care!