Understanding Reflection in Go: Types, Kinds, and Dynamic Operations

Reflection in Go provides runtime access to type information and value manipulation, but it comes with performance and safety trade-offs. This article explores the core concepts of reflection—reflect.Type, reflect.Value, and Kind—and demonstrates practical applications with slices, maps, structs, pointers, and functions.

Core Concepts: Type, Value, and Kind

Reflecsion in Go revolves around two primary types and a concept called Kind:

  • reflect.Type: Represents the metadata of any Go type, including its name, package path, methods, and underlying Kind. It provides static type information but does not allow direct value manipulation.
  • reflect.Value: Represents a runtime value, encapsulating both its type and its data. It enables reading and, when addressable, modifying the underlying value.
  • Kind: An enumeration (reflect.Kind) that categorizes a type or value into a specific class, such as reflect.Int, reflect.String, reflect.Slice, reflect.Struct, reflect.Ptr, or reflect.Interface. It covers both primitive and composite types.

For example:

var x int = 10
t := reflect.TypeOf(x)   // t is of type reflect.Type, representing int
v := reflect.ValueOf(x)  // v is of type reflect.Value, holding 10
k := v.Kind()           // k is reflect.Int

Reflection with Slices

A slice in Go contains a pointer to an underlying array, a length, and a capacity. Reflection enables dynamic inspection and modification of slices.

// Underlying slice header structure
type sliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

Basic Operations

s := []int{1, 2, 3}
typ := reflect.TypeOf(s)   // []int
val := reflect.ValueOf(s)  // value of slice

Accessing and Modifying Elements

Reflection allows reading and, if the value is settable, modifying slice elements.

if val.CanSet() {
    elem := val.Index(0)
    elem.SetInt(42)  // changes first element to 42
}

Replacing the Entire Slice

To replace the whole slice, use Set with a new slice of the same type.

newSlice := []int{4, 5, 6}
val.Set(reflect.ValueOf(newSlice))

Length and Capacity

length := val.Len()
capacity := val.Cap()

Note that reflection does not provide a direct append equivalent; a new slice must be created and elements copied manually.

Reflection with Maps

Maps in Go are hash-based key-value collections. Reflection allows dynamic type inspection and content manipulation.

Checking Map Type

m := map[string]int{"a": 1}
typ := reflect.TypeOf(m)
if typ.Kind() == reflect.Map {
    keyType := typ.Key()     // string
    valueType := typ.Elem()  // int
}

Iterating and Modifying Entries

val := reflect.ValueOf(m)
for _, key := range val.MapKeys() {
    value := val.MapIndex(key)
    fmt.Println("Key:", key.Interface(), "Value:", value.Interface())

    // Modify value (must be settable)
    if value.CanSet() {
        val.SetMapIndex(key, reflect.ValueOf(2))
    }
}

Creating New Map Instances

Reflection can create new map instances dynamically when the key and value type are known at runtime.

Reflection with Structs

Structs are composite types with named fields. Reflection provides powerful runtime introspection and manipulation.

Accessing Type and Value

type Person struct {
    Name string
    Age  int
}

p := Person{"Alice", 30}
typ := reflect.TypeOf(p)
val := reflect.ValueOf(p)

Iterating Fields

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

Reading and Modifying Field Values

nameField := val.FieldByName("Name")
if nameField.IsValid() && nameField.CanSet() {
    nameField.SetString("Bob")
}

Working with Struct Tags

Tags (e.g., JSON) are accessible via reflection.

tag := typ.Field(0).Tag.Get("json")
fmt.Println("JSON tag:", tag)

Calling Struct Methods

Methods can be discovered and invoked dynamically.

Reflection with Pointers

Pointers enable indirect memory access. Combined with reflection, they enable modification of underlying values even when passed as interfaces.

Dereferencing Pointers

var i int = 42
ptr := &i
v := reflect.ValueOf(ptr).Elem()  // dereferences the pointer
v.SetInt(1337)
fmt.Println(i)  // 1337

Modifying Private Struct Fields via Pointer

Reflection can access unexported fields if a pointer to the struct is used.

type Data struct {
    secret string
}

d := &Data{"initial"}
v := reflect.ValueOf(d).Elem()
field := v.FieldByName("secret")
if field.IsValid() && field.CanSet() {
    field.SetString("modified")
}

Creating New Pointer Values

type MyType struct{}
newInstance := reflect.New(reflect.TypeOf(MyType{})).Elem()

Reflection with Functions

Reflection enables dynamic function invocation and signature inspection.

Getting Function Type

add := func(a, b int) int { return a + b }
typ := reflect.TypeOf(add)
fmt.Println(typ)  // func(int, int) int

Calling a Function Dynamically

fn := reflect.ValueOf(add)
params := []reflect.Value{reflect.ValueOf(3), reflect.ValueOf(5)}
results := fn.Call(params)
fmt.Println(results[0].Interface())  // 8

Inspecting Function Signature

numIn := typ.NumIn()
numOut := typ.NumOut()
for i := 0; i < numIn; i++ {
    fmt.Println("Input type:", typ.In(i))
}
for i := 0; i < numOut; i++ {
    fmt.Println("Output type:", typ.Out(i))
}

Handling Methods

Reflection can identify receiver types of methods.

type T struct{}
func (T) M() {}
m := reflect.ValueOf(T.M)
methodType := m.Type()
if methodType.NumIn() > 0 {
    fmt.Println("Receiver type:", methodType.In(0))
}

Summary of Reflection Capabilities

Reflection in Go allows runtime access to:

  • Type metadata (name, kind, fields, methods, tags)
  • Value reading and modification (when addressable)
  • Dynamic method calls
  • Creating new instances of types
  • Iterating over collections (slices, maps, arrays)
  • Cross-package access to unexported members (discouraged)

While reflection adds significant flexibility for generic utilities, serialization frameworks, and dynamic dispatch, it should be used sparingly due to performance costs and potential safety risks.

Tags: Go Golang reflection runtime type reflect.Type

Posted on Sun, 10 May 2026 13:03:23 +0000 by ScottCFR