Using reflect, how do you set the value of a struct field?

GoReflectionGo Reflect

Go Problem Overview


having a rough time working with struct fields using reflect package. in particular, have not figured out how to set the field value.

type t struct { fi int; fs string }
var r t = t{ 123, "jblow" }
var i64 int64 = 456

  1. getting Name of field i - this seems to work

var field = reflect.TypeOf(r).Field(i).Name

  1. getting value of field i as a) interface{}, b) int - this seems to work

var iface interface{} = reflect.ValueOf(r).Field(i).Interface()

var i int = int(reflect.ValueOf(r).Field(i).Int())

  1. setting value of field i - try one - panic

reflect.ValueOf(r).Field(i).SetInt( i64 )

panic: reflect.Value·SetInt using value obtained using unexported field

assuming it did not like field names "id" and "name", so renamed to "Id" and "Name"

a) is this assumption correct?

b) if correct, thought not necessary since in same file / package

  1. setting value of field i - try two (with field names capitalized ) - panic

reflect.ValueOf(r).Field(i).SetInt( 465 )

reflect.ValueOf(r).Field(i).SetInt( i64 )

panic: reflect.Value·SetInt using unaddressable value


Instructions below by @peterSO are thorough and high quality

Four. this works:

reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )

he documents as well that the field names must be exportable (begin with capital letter)

Go Solutions


Solution 1 - Go

The Go json package marshals and unmarshals JSON from and to Go structures.

Here's a step-by-step example which sets the value of a struct field while carefully avoiding errors.

The Go reflect package has a CanAddr function.

func (v Value) CanAddr() bool

> CanAddr returns true if the value's > address can be obtained with Addr. > Such values are called addressable. A > value is addressable if it is an > element of a slice, an element of an > addressable array, a field of an > addressable struct, or the result of > dereferencing a pointer. If CanAddr > returns false, calling Addr will > panic.

The Go reflect package has a CanSet function, which, if true, implies that CanAddr is also true.

func (v Value) CanSet() bool

> CanSet returns true if the value of v > can be changed. A Value can be changed > only if it is addressable and was not > obtained by the use of unexported > struct fields. If CanSet returns > false, calling Set or any > type-specific setter (e.g., SetBool, > SetInt64) will panic.

We need to make sure we can Set the struct field. For example,

package main

import (
	"fmt"
	"reflect"
)

func main() {
	type t struct {
		N int
	}
	var n = t{42}
	// N at start
	fmt.Println(n.N)
	// pointer to struct - addressable
	ps := reflect.ValueOf(&n)
	// struct
	s := ps.Elem()
	if s.Kind() == reflect.Struct {
		// exported field
		f := s.FieldByName("N")
		if f.IsValid() {
			// A Value can be changed only if it is 
			// addressable and was not obtained by 
			// the use of unexported struct fields.
			if f.CanSet() {
				// change value of N
				if f.Kind() == reflect.Int {
					x := int64(7)
					if !f.OverflowInt(x) {
						f.SetInt(x)
					}
				}
			}
		}
	}
	// N at end
	fmt.Println(n.N)
}

Output:
42
7

If we can be certain that all the error checks are unnecessary, the example simplifies to,

package main

import (
	"fmt"
	"reflect"
)

func main() {
	type t struct {
		N int
	}
	var n = t{42}
	fmt.Println(n.N)
	reflect.ValueOf(&n).Elem().FieldByName("N").SetInt(7)
	fmt.Println(n.N)
}

BTW, Go is available as open source code. A good way to learn about reflection is to see how the core Go developers use it. For example, the Go fmt and json packages. The package documentation has links to the source code files under the heading Package files.

Solution 2 - Go

This seems to work:

package main

import (
	"fmt"
	"reflect"
)

type Foo struct {
	Number int
	Text string
}

func main() {
	foo := Foo{123, "Hello"}

	fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))

	reflect.ValueOf(&foo).Elem().Field(0).SetInt(321)

	fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
}

Prints:

123
321

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questioncc youngView Question on Stackoverflow
Solution 1 - GopeterSOView Answer on Stackoverflow
Solution 2 - GoAsgeirView Answer on Stackoverflow