How to use reflect to set every field of struct to non-nil value in go

Viewed 527

Suppose I have some type, and I want to instantiate a variable of this type, with every value non-nil.

type Event struct {
    HappenedAtMs   int64 
    ReceivedAtMs   int64
    FieldA         *FieldAType
    FieldB         []*FieldBType

Here is what I am currently trying:

    eventFields := reflect.TypeOf(Event{})

    event := Event{}
    for i := 0; i < eventFields.NumField(); i++ {
        nonEmptyType := reflect.New(eventFields.Field(i).Type).Elem()
        reflect.ValueOf(&event).Elem().Field(i).Set(nonEmptyType)
    }

However, upon running this code, all the fields in the event variable are still set to nil. How can I achieve what I want?

1 Answers

The reflect package needs a pointer to the struct for it to be able to set its fields. The fields also need to be exported which you can check against using the CanSet method.

To initialize a pointer type with reflect you can simply do reflect.New(T.Elem()). To initialize a map, a slice, a func, or a chan type to non-nil you can use the MakeMap, MakeSlice, MakeFunc, and MakeChan functions respectively. To initialize an interface type to non-nil you can create an anonymous struct type, using reflect.StructOf, with a single embedded field of the target interface type, by embedding the interface type the struct type automatically satisfies the interface and an instance of it can be used to set the field to non-nil.

event := Event{}

rv := reflect.ValueOf(&event).Elem()
for i := 0; i < rv.NumField(); i++ {
    if f := rv.Field(i); isNilable(f) && f.IsNil() && f.CanSet() {
        switch f.Kind() {
        case reflect.Ptr:
            f.Set(reflect.New(f.Type().Elem()))
        case reflect.Slice:
            f.Set(reflect.MakeSlice(f.Type(), 0, 0))
        case reflect.Interface:
            sf := reflect.StructField{
                Name:      f.Type().Name(),
                Type:      f.Type(),
                Anonymous: true,
            }
            rt := reflect.StructOf([]reflect.StructField{sf})
            f.Set(reflect.New(rt).Elem())
            // TODO handle the rest of nilable types
        }
    }
}

https://play.golang.com/p/nQqvUIROqF-

Related