๐ŸŽ“ A1Learner
๐Ÿ”
0 of 28 lessons completed

Go Reflection Values

Reflection values are handles that can read and change a value while the program runs. A handle can also run a method by naming it as a string.

Setting a field

The handle starts from the address of the struct. SetInt writes 150 into the Pages field.

package main

import (
	"fmt"
	"reflect"
)

type Book struct {
	Title string
	Pages int
}

func main() {
	b := Book{Title: "Go", Pages: 100}
	fmt.Println(b.Pages)
	reflect.ValueOf(&b).Elem().Field(1).SetInt(150)
	fmt.Println(b.Pages)
}

Asking CanSet

The handle from a pointer can set the field. The handle from a plain copy cannot set it.

package main

import (
	"fmt"
	"reflect"
)

type Book struct {
	Title string
	Pages int
}

func main() {
	b := Book{Title: "Go", Pages: 100}
	fmt.Println(reflect.ValueOf(&b).Elem().Field(1).CanSet())
	fmt.Println(reflect.ValueOf(b).Field(1).CanSet())
}

A write that panics

This program builds the handle from a plain copy. The SetInt call panics and the program stops.

package main

import (
	"fmt"
	"reflect"
)

type Book struct {
	Title string
	Pages int
}

func main() {
	b := Book{Title: "Go", Pages: 100}
	reflect.ValueOf(b).Field(1).SetInt(150)
	fmt.Println(b.Pages)
}

Calling a method by name

MethodByName looks the method up as a string. Call runs it with no arguments and returns the answer.

package main

import (
	"fmt"
	"reflect"
)

type Book struct {
	Title string
	Pages int
}

func (b Book) Summary() string {
	return fmt.Sprintf("%s has %d pages", b.Title, b.Pages)
}

func main() {
	b := Book{Title: "Go", Pages: 120}
	out := reflect.ValueOf(b).MethodByName("Summary").Call(nil)
	fmt.Println(out[0].String())
}

Example

package main

import (
	"fmt"
	"reflect"
)

type Book struct {
	Title string
	Pages int
}

func raise(v any, name string, value int64) {
	reflect.ValueOf(v).Elem().FieldByName(name).SetInt(value)
}

func main() {
	b := Book{Title: "Go", Pages: 100}
	fmt.Println(b.Pages)
	raise(&b, "Pages", 180)
	fmt.Println(b.Pages)
}

Key points

  • SetInt writes a new value into an int field.
  • A field must be addressable before a handle can set it.
  • CanSet reports whether a handle can change its value.
  • Setting an unaddressable value panics at run time.
  • MethodByName finds a method from its name string.
  • Call runs the method and returns its results in a slice.

In this example, raise takes a struct pointer, a field name, and a new value. It finds the field by name through reflection. It then sets the field to 180 and main prints the new count.

Note

Passing a pointer to a struct makes its fields addressable.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Go Playground

Exercise

What must a struct value have before reflection can set its fields?