1

I'm not quite sure how to address this question, please feel free to edit.

With the first code block below, I am able to check if a all fields of a struct are nil.

In reality however, the values injected in the struct, are received as args.Review (see second code block below).

In the second code block, how can I check if all fields from args.Review are nil?

Try it on Golang Playground

package main

import (
    "fmt"
    "reflect"
)

type review struct {
    Stars      *int32  `json:"stars" bson:"stars,omitempty" `
    Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}

func main() {

    newReview := &review{
        Stars: nil,
        // Stars:   func(i int32) *int32 { return &i }(5),

        Commentary: nil,
        // Commentary: func(i string) *string { return &i }("Great"),
    }

    if reflect.DeepEqual(review{}, *newReview) {
        fmt.Println("Nothing")
    } else {
        fmt.Println("Hello")
    }

}

Try the second code on Golang Playground This code below gets two errors:

prog.go:32:14: type args is not an expression

prog.go:44:27: args.Review is not a type

package main

import (
    "fmt"
    "reflect"
)

type review struct {
    Stars      *int32  `json:"stars" bson:"stars,omitempty" `
    Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}

type reviewInput struct {
    Stars      *int32
    Commentary *string
}

type args struct {
    PostSlug string
    Review   *reviewInput
}

func main() {
    f := &args {
        PostSlug: "second-post",
        Review: &reviewInput{
            Stars:  func(i int32) *int32 { return &i }(5),
            Commentary: func(i string) *string { return &i }("Great"),
        },

    }
    createReview(args)
}

func createReview(args *struct {
    PostSlug string
    Review   *reviewInput
}) {
    g := &review{
        Stars: args.Review.Stars,
        Commentary: args.Review.Commentary,
    }

    if reflect.DeepEqual(args.Review{}, nil) {
        fmt.Println("Nothing")
    } else {
        fmt.Println("Something")
    }
}
aquiseb
  • 949
  • 8
  • 17
  • 1
    Your second code example contains a bunch of invalid Go code, it's hard to tell whether you have difficulties writing valid Go or whether you have difficulties solving your actual problem. Listen to the compiler errors and fix your code. To answer your actual question, you can use the `reflect` package, with that you can loop over an arbitrary struct type's fields and check their values individually. – mkopriva Mar 24 '18 at 15:43
  • @mkopriva I have edited the second code block, hopefully my question is more clear now – aquiseb Mar 24 '18 at 19:10
  • 1
    Are you able to instantiate the `reviewInput` type yourself or is that an unexported type from a 3rd party package that does not provide a simple way to construct it? Because if you are, then it doesn't matter the least bit that the value you want to check is a field in the args type, just do what you're doing in the first code example. – mkopriva Mar 24 '18 at 19:17
  • 1
    ...anyways the way you're approaching it, with `DeepEqual`, feels very weird to me (personal opinion), if you care take a look at this example: https://play.golang.org/p/nnf-VPIQwkk – mkopriva Mar 24 '18 at 19:19
  • 1
    Also I'm assuming that you have a lot more than just two fields to check and you're looking for a way to avoid writing `ifs` for each field individually. If that's not the case, if you have only two fields or even a bit more, please don't use reflect, use `if v.f == nil && v.g == nil {` instead, it's much more clearer what's happening and it's also the more Go-ish approach. – mkopriva Mar 24 '18 at 19:24
  • Thank you very much. `reviewInput` is indeed instantiated through github.com/neelance/graphql-go as a mutation, I was trying to get as close as possible to illustrate my issue. You're absolutely right, checking `newReview` from the first code block does give me the information, however that's what I'm worried about: is it not better practice to check `args.Review` instead of first injecting its fields into `newReview` first? Thank you for your tip about `DeepEqual` and advising to not use reflect. Please feel free to write an answer that wraps it all so I can upvote and mark as accepted answer – aquiseb Mar 24 '18 at 19:35
  • Just a little clarification regarding the go play snippet you've sent and in case you are wrapping all comments in a complete answer; I'm trying to know if all fields are nil, not trying to know if each field is nil (one by one), but the line between both is very thin I know ;) – aquiseb Mar 24 '18 at 19:50

1 Answers1

6

If you're dealing with a small number of fields you should use simple if statements to determine whether they are nil or not.

if args.Stars == nil && args.Commentary == nil {
    // ...
}

If you're dealing with more fields than you would like to manually spell out in if statements you could use a simple helper function that takes a variadic number of interface{} arguments. Just keep in mind that there is this: Check for nil and nil interface in Go

func AllNil(vv ...interface{}) bool {
    for _, v := range vv {
        if v == nil {
            continue
        }

        if rv := reflect.ValueOf(v); !rv.IsNil() {
            return false
        }
    }
    return true
}

if AllNil(args.Stars, args.Commentary, args.Foo, args.Bar, args.Baz) {
    // ...
}

Or you can use the reflect package to do your bidding.

func NilFields(x interface{}) bool {
    rv := reflect.ValueOf(args)
    rv = rv.Elem()

    for i := 0; i < rv.NumField(); i++ {
        if f := rv.Field(i); f.IsValid() && !f.IsNil() {
            return false
        }
    }
    return true
}

if NilFields(args) {
   // ...
}
mkopriva
  • 35,176
  • 4
  • 57
  • 71