How to initialize nil slices in a struct using reflect - go

For any given struct, I want to loop over its fields and set any nil slices to an empty slice. However, slices are unaddressable and hence not settable. How can I set the values of any nil slices?
Example:
https://goplay.space/#iV6OHkYVTru
package main
import (
"fmt"
"reflect"
)
type Foo struct {
IntSlice []int
StrSlice []string
}
func main() {
foo := Foo{}
fmt.Println(foo.IntSlice == nil)
initNilSlices(foo)
fmt.Println(foo.IntSlice == nil)
}
func initNilSlices(x interface{}) {
v := reflect.ValueOf(x)
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
if f.Kind() == reflect.Slice {
t := f.Type()
f.Set(reflect.MakeSlice(t, 0, 0))
}
}
}

You can't update / modify foo in initNilSlices() if you pass foo because that will be a copy, and you could only modify the copy (and not the original value).
This is what the error message "hints":
panic: reflect: reflect.Value.Set using unaddressable value
You obtained the reflect.Value from a non-pointer value, and even what reflect.ValueOf() gets is a copy, so it doesn't allow you to modify it because it wouldn't be what you'd want.
You have to pass the address of foo:
initNilSlices(&foo)
And in initNilSlices() use Value.Elem():
v := reflect.ValueOf(x).Elem()
With this it works and outputs (try it on the Go Playground):
true
false

Related

check if interface{} is slice of something ([]interface{})

func main() {
strSlice := []string{"a", "b", "c"}
f(strSlice)
}
func f(slice interface{}) {
anySlice, isSlice := slice.([]interface{})
fmt.Printf("isSlice = %t, anySlice = %#v\n", isSlice, anySlice)
}
Playground: https://play.golang.org/p/UN25mIOqmOd
This program prints isSlice = false, anySlice = []interface {}(nil). Why is that?
I would have expected this type asssertion to be possible.
And: Is there a way to dynamically check that interface{} is a slice of something?
The type assertion fails because a []string is not an []interface{}. See the FAQ for details.
Use the reflect package to determine if the concrete value in an interface is a slice:
func f(slice interface{}) {
isSlice := reflect.ValueOf(slice).Kind() == reflect.Slice
fmt.Printf("isSlice = %t, anySlice = %#v\n", isSlice, slice)
}
Run it on the playground.
You can also use the reflect package to iterate through the values:
v := reflect.ValueOf(slice)
isSlice := v.Kind() == reflect.Slice
if isSlice {
for i := 0; i < v.Len(); i++ {
fmt.Printf("%d: %v\n", i, v.Index(i).Interface())
}
}
Run it on the playground.
This program prints isSlice = false, anySlice = []interface {}(nil). Why is that?
because slice doesn't contain a []interface{}, it contains a []string. Those are different types, and Go doesn't give you any notion of covariant container types.
And: Is there a way to dynamically check that interface{} is a slice of something?
Yes, you can use reflection:
func f(slice interface{}) {
typ := reflect.TypeOf(slice)
if typ.Kind() == reflect.Slice {
elemType := typ.Elem()
fmt.Println("slice of", elemType.Name())
} else {
fmt.Println("not a slice")
}
}
Actually doing anything with that information may be more involved.
Notice that if you change your assertion to: slice.([]string) things will work as expected. This makes sense when you consider how Go handles string to interface{} type assertions. The following will result in a compilation error:
package main
func main() {
s := "some string"
i := s.(interface{})
}
Error:
invalid type assertion: s.(<inter>) (non-interface type string on left)
It makes sense that going from []string to []interface{} should also fail since Go does not treat strings as interface{} types.

Copying field/value from src to dest object

I am trying to copy the fields from one struct value to another, where they have the same field definitions. I have this program:
package main
import (
"log"
"reflect"
)
func setExistingFields(src interface{}, dst interface{}) {
fields := reflect.TypeOf(src)
values := reflect.ValueOf(src)
num := fields.NumField()
s := reflect.ValueOf(src).Elem()
d := reflect.ValueOf(dst).Elem()
for i := 0; i < num; i++ {
field := fields.Field(i)
value := values.Field(i)
fsrc := s.FieldByName(field.Name)
fdest := d.FieldByName(field.Name)
if fdest.IsValid() && fsrc.IsValid() {
if fdest.CanSet() && fsrc.CanSet() {
fdest.Set(value)
}
}
}
}
// and then we main:
func main() {
src := struct {
Foo string
Bar string
}{
"dog",
"pony",
}
dest := struct{ Foo string; Bar string }{}
setExistingFields(&src, &dest)
log.Println("dest.Foo", dest.Foo)
}
I run that, but I get an error:
reflect: NumField of non-struct type
I can't figure out what that's about.
Here's a playground link:
https://play.golang.org/p/TsHTfAaeKhc
Try this out:
func setExistingFields(src interface{}, dst interface{}) {
srcFields := reflect.TypeOf(src).Elem()
srcValues := reflect.ValueOf(src).Elem()
dstValues := reflect.ValueOf(dst).Elem()
for i := 0; i < srcFields.NumField(); i++ {
srcField := srcFields.Field(i)
srcValue := srcValues.Field(i)
dstValue := dstValues.FieldByName(srcField.Name)
if dstValue.IsValid() {
if dstValue.CanSet() {
dstValue.Set(srcValue)
}
}
}
}
Note that you need to do additional checking if src field value is assignable to dst field type.
Edit: The reason why you are getting that error is because fields at that point is a pointer to a struct. You need to get the actual struct value by using Elem().
This won't work: A struct always gets its "schema" (eg. its fields) during compile time... You cannot add more fields during runtime.
I don't see what your exact use case is, but consider something like map[string]string or even map[string]interface{} to be able to "extend" the content/fields of the thing you are passing around...

How to convert interface to interfaces slice?

My input is an interface{}, and I know it can be an array of any type.
I'd like to read one of the elements of my input, so I try to convert my interface{} into an []interface{}, but go will give me the following error:
panic: interface conversion: interface {} is []map[string]int, not []interface {}
How can I do that conversion? (without reflect if possible).
Playground test
Thanks
The solution involving the reflect package.
package main
import (
"fmt"
"reflect"
)
func main() {
var v interface{} = []string{"a", "b", "c"}
var out []interface{}
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Slice {
for i := 0; i < rv.Len(); i++ {
out = append(out, rv.Index(i).Interface())
}
}
fmt.Println(out)
}
// Output:
// [a b c]
I'm actually working on this right now as my issue involves taking something from a json object (map[string]interface{}) which may or may not contain a particular key ({"someKey": [a, b, c, ...]) and if it does contain that key then we want to take that (which will necessarily be interface{} type) and convert it to []interface{}. The method I've found so far is to use json marshall/unmarshall. This seems a little hacky to me, will update if I find a more elegant solution. Til then, you can have my method:
https://play.golang.org/p/4VAwQQE4O0b
type a map[string]interface{}
type b []string
func main() {
obj := a{
"someKey": b{"a", "b", "c"},
}
if obj["someKey"] != nil { // check the value exists
var someArr []interface{}
//marshal interface to byte and then unmarshal to []interface{}
somebytes, _ := json.Marshal(obj["someKey"])
err := json.Unmarshal(somebytes, &someArr)
if err != nil {
fmt.Println("Error in unmarshal")
}
fmt.Println(someArr)
}
}
How can I do that conversion? (without reflect if possible).
Please consider type switches.
Reflection is expensive.
func toSlice(i interface{}) []interface{} {
var out []interface{}
switch v := i.(type) {
case []interface{}:
for x := 0; x < len(v); x++ {
out = append(out, v[x])
}
default:
fmt.Printf("invalid type: %T\n", v)
}
return out
}
The point of the interface is to define the behaviour you want to use, if you use an empty interface, you know nothing about the types in that slice.
If you want to print it, you can use println or printf with no conversion.
If you want to access it, and must allow any type, you can use reflect (slow and complex to use).
If you want to acess it, and use common behaviour/ data that you can define functions for, define an interface, e.g. :
type Doer interface {
Do() error
}
parentStruct := []Doer{...}
testStruct.Do()
If none of that works, wait for Go 2 and generics.
For anyone finding this in 2022, now that we have generics you can do it like this:
func convertSlice[T any](data []T) []interface{} {
output := make([]interface{}, len(data))
for idx, item := range data {
output[idx] = item
}
return output
}
I think what you are looking is type assertion
package main
import (
"fmt"
)
func main() {
parentStruct := map[string]interface{}{
"test": []map[string]int{
{"a": 1, "b": 2},
{"c": 3},
},
}
testStruct := parentStruct["test"].([]map[string]int)
fmt.Println(testStruct)
}
read this link: https://golang.org/ref/spec#Type_assertions
https://play.golang.org/p/81uL2hgrN3l

Set pointer to struct passed in via interface{} to nil using reflect in Golang?

I am trying to use reflect to set a pointer to a struct to nil passed in through an interface{} in Go.
I have this example program but it always prints false as a is not set to nil. What am I doing wrong?
package main
import "reflect"
type MyStruct struct {}
func main() {
a := &MyStruct{}
wipePassed(a)
println(a == nil)
}
func wipePassed(r interface{}){
v := reflect.ValueOf(&r)
p := v.Elem()
p.Set(reflect.Zero(p.Type()))
}
You can't with your current code. The pointer a is passed (as if) by value even though it's being wrapped in an interface, and there's no way to zero the original copy. This is just the same as the case when a is a non-pointer type (for example an int); there's no function f that can change the value of a in the code a := 42; f(a) -- you have to pass a pointer.
Your code can work if you pass in the address of the pointer a:
package main
import "reflect"
type MyStruct struct{}
func main() {
a := &MyStruct{}
wipePassed(&a)
println(a == nil)
}
func wipePassed(r interface{}) {
v := reflect.ValueOf(r)
p := v.Elem()
p.Set(reflect.Zero(p.Type()))
}

How do you set a value to a pointer containing nil using reflection

I'm trying to set a value to a nil pointer in a struct like so.
// https://play.golang.org/p/jPTMNC_ZQ9
package main
import (
"fmt"
"reflect"
)
type T struct {
A *int
}
func main() {
fmt.Println("Hello, playground")
t := &T{}
v := 1
vptr := &v
CopyValue(vptr, t.A) // I want to set t.A to contain 1
}
func CopyValue(src interface{}, dest interface{}) {
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr {
srcRef = srcRef.Elem()
}
destRef := reflect.New(srcRef.Type()).Elem()
destRef.Set(srcRef)
reflect.ValueOf(dest).Elem().Set(destRef)
}
However, I encounter the following error:
panic: reflect: call of reflect.Value.Set on zero Value
goroutine 1 [running]:
reflect.flag.mustBeAssignable(0x0, 0x1040a128)
/usr/local/go/src/reflect/value.go:221 +0x260
reflect.Value.Set(0x0, 0x0, 0x0, 0xdefc0, 0x1040a128, 0x182)
/usr/local/go/src/reflect/value.go:1339 +0x40
main.CopyValue(0xd7860, 0x1040a124, 0xd7860, 0x0)
/tmp/sandbox487854080/main.go:30 +0x1a0
main.main()
/tmp/sandbox487854080/main.go:19 +0x100
What am I doing wrong?
In order to be able to modify what t.A points to, you need to send a reference to it to your CopyValue function.
CopyValue(vptr, &t.A) // (note the &)
You can then assign the pointer to the new address:
func CopyValue(src interface{}, dest interface{}) {
srcRef := reflect.ValueOf(src)
vp := reflect.ValueOf(dest)
vp.Elem().Set(srcRef)
}
See the 3rd "law of reflection" here: https://blog.golang.org/laws-of-reflection
Full working code:
package main
import (
"fmt"
"reflect"
)
type T struct {
A *int
}
func main() {
t := &T{}
v := 1
vptr := &v
CopyValue(vptr, &t.A) // we pass a reference to t.A since we want to modify it
fmt.Printf("%v\n", *t.A)
}
func CopyValue(src interface{}, dest interface{}) {
srcRef := reflect.ValueOf(src)
vp := reflect.ValueOf(dest)
vp.Elem().Set(srcRef)
}
reflect.ValueOf(dest).Elem().Set(destRef)
If you look into this line, reflect.ValueOf(dest) will give you nil, since you passed in a nil pointer. Calling .Elem() on this is invalid, since there is no element to the nil pointer.
t.A is a nil pointer when you pass it in, so CopyValue is being asked to copy a value into an invalid (nil) location. You need to allocate space for an int for it to point to, and then CopyValue will be able to do the copy into the location pointed at.
This resolves the error and allows the value to be copied:
t := &T{}
t.A = new(int) // Add this line

Resources