Skip to content

Go Reflection

Reflection allows a Go program to inspect its own structure (types and values) at runtime. This is handled by the reflect package.

1. Type and Value

The two most important concepts are reflect.Type and reflect.Value.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    x := 3.4

    // 1. Get the Type (float64)
    fmt.Println("type:", reflect.TypeOf(x))

    // 2. Get the Value (3.4)
    v := reflect.ValueOf(x)
    fmt.Println("value:", v)
    fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
}

2. Inspecting Structs

Reflection is commonly used to look inside structs, read their field names, and check their "tags."

type User struct {
    ID   int    `json:"id"`
    Name string `json:"user_name"`
}

u := User{1, "Alice"}
t := reflect.TypeOf(u)

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

3. Modifying Values

To modify a value through reflection, you must pass a pointer to the data and use Elem().

1
2
3
4
5
6
7
x := 10
v := reflect.ValueOf(&x).Elem() // Get the value the pointer points to

if v.CanSet() {
    v.SetInt(20)
}
fmt.Println(x) // 20

Why use Reflection?

  1. Generic Libraries: Packages like encoding/json use reflection because they don't know the type of your struct until the program is running.
  2. Object Mapping: Libraries that map database rows to Go structs.
  3. Validation: Checking struct tags (e.g., validate:"required") at runtime.

Downsides

  • Performance: Reflection is much slower than regular code.
  • Type Safety: Reflection bypasses compile-time checks. If you try to set a string field with an int, the program will panic at runtime.
  • Complexity: Reflection code is often harder to read and maintain.