Home > Software design >  Operate on a struct with lots of pointers (especially to numbers)
Operate on a struct with lots of pointers (especially to numbers)

Time:04-23

I have quite a few data structures that contain mostly numbers, I get the data, do a calculation and return the result.

The thing is that all of those numbers can be zero and hence, I had to switch to using pointers (*int64 or *float64) so that the default is nil and not 0.

Unfortunately, I don't know of a solution to this in Go except using pointers.

The problem comes now in the Calculate() function that is implemented for all data structures:

type X struct {
    A, B, C, D, E, F *int
    // and much more

    Result *float64
}

func (x *X) Calculate() {
    floatptr := func(f float64) *float64 { return &f }
    x.Result = floatptr(float64(*x.A   *x.B   *x.C   *x.D   *x.E   *x.F))
}

This function will obviously panic if any of the data is nil. So, I wrote the functions differently that it checks for nil data before the calculation:

func (x *X) CalculateWithNilChecks() {
    floatptr := func(f float64) *float64 { return &f }
    if x.A == nil || x.B == nil || x.C == nil || x.D == nil || x.E == nil || x.F == nil {
        return
    }
    x.Result = floatptr(float64(*x.A   *x.B   *x.C   *x.D   *x.E   *x.F))
}

The problem is that the data structures are quite long. Having a SUPER long if x != nil looks ugly. I was wondering if there is another (cleaner) way to doing this.


  • I thought of doing like in the encoding/json and just recover nil pointer dereference panics, not sure if this is cleaner TBH.
  • Another thought was to reflect the data structures and stop if any of the required data is nil, I don't think this should be necessary for such a simple task.

Here is a playground link for the above code

What am I missing here? Thanks!

CodePudding user response:

As general solution you can unmarshal your JSON into a map of *int pointers or json.RawMessage and then use a reflection to cross check with your struct or just check it with expected number of fields.

func main() {
    result := make(map[string]*int)
    str := `{ "A": 1, "B": 2, "C": 3, "D": 4, "E": 5, "F": 6 }`
    json.Unmarshal([]byte(str), &result)
    for _, field := range result {
        // Check if expect fields exists using reflection or manually
        // ...
    }
}

CodePudding user response:

You can use reflect module and error when one of the required fields is missing.

Use this as template.

package main

import (
    "fmt"
    "reflect"
)

type X struct {
    A, B, C, D, E, F *int

    Result *float64
}

func (x *X) PrintFoo() {
    fmt.Println(x.A)
}

func main() {
    a := 3
    x := X{A: &a}

    val := reflect.ValueOf(x)
    for i := 0; i < val.Type().NumField(); i   {
        field := val.Type().Field(i)
        fieldType := fmt.Sprintf("%s", field.Type)

        if fieldType == "*int" && val.FieldByName(field.Name).IsNil() {
            fmt.Println("Missing value on field", field.Name)
        }
    }
}

  •  Tags:  
  • go
  • Related