Re: [go-nuts] Checking struct members for nil pointers

3,604 views
Skip to first unread message

Matt Kane's Brain

unread,
Aug 20, 2012, 1:31:05 PM8/20/12
to Jeff Mitchell, golan...@googlegroups.com
Oh, Tomahawk uses Go now? Nice :)

On Mon, Aug 20, 2012 at 1:19 PM, Jeff Mitchell
<jeff...@tomahawk-player.org> wrote:
> However, I've had some trouble actually pulling this off. I figured the
> right way to go would be to use the reflect package. I've been able to
> iterate through the members of a struct, accessing reflect.StructField
> structs, but what I haven't been able to figure out is the right way to
> actually get the values of these fields. The StructField structs have the
> name of the field but don't seem to have the value.

You could call Unmarshal like this:
var t interface{}
json.Unmarshal(str, &t)

Now t is a newly allocated map[string]interface{}, and you can test
for a field's presence with something like:
tm := t.(map[string]interface{})
for k := range fieldsicareabout {
_, present := tm[k]
if present {
// yay
} else {
// boo
}
}

--
matt kane's brain
http://hydrogenproject.com

Jeff Mitchell

unread,
Aug 20, 2012, 1:54:10 PM8/20/12
to golan...@googlegroups.com, Jeff Mitchell
On Monday, August 20, 2012 1:31:05 PM UTC-4, mkb wrote:
Oh, Tomahawk uses Go now? Nice :)

I'm attempting to use it for some backend work. That's all I'll say for now  :-) 
 
You could call Unmarshal like this:
var t interface{}
json.Unmarshal(str, &t)

Now t is a newly allocated map[string]interface{}, and you can test
for a field's presence with something like:
tm := t.(map[string]interface{})
for k := range fieldsicareabout {
    _, present := tm[k]
    if present {
      // yay
    } else {
      // boo
    }
}

So I figured I could do that, but that requires me hardcoding the list of fields to check for. I was hoping for something a bit more dynamic, since the struct members may change. That way I could update the struct in one place but not then have to modify this checking code, simply keep the same check-for-any-nil-pointers function that operates on any struct regardless of the fields.

Jeff Mitchell

unread,
Aug 20, 2012, 2:10:17 PM8/20/12
to swh...@ngmoco.com, golan...@googlegroups.com
On Mon, Aug 20, 2012 at 1:53 PM, <swh...@ngmoco.com> wrote:
I think reflect is what you want and the Value.Field* methods to get the Value.

Looking at it again, if I use Value.Field(i) method to iterate through the Values corresponding to the struct fields, and then use the Pointer method to get the pointer value, which I could check for nil. But the text from reflect.Pointer is "It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer." I saw that before and it doesn't seem like a struct member that is a pointer is any of those, so if that's the right way to do it (is it?) it threw me off.

Thanks,
Jeff

Matt Kane's Brain

unread,
Aug 20, 2012, 2:23:48 PM8/20/12
to swh...@ngmoco.com, golan...@googlegroups.com
Yup: http://play.golang.org/p/5Squ3mr7K7

On Mon, Aug 20, 2012 at 2:18 PM, <swh...@ngmoco.com> wrote:
> I think the "Ptr" Kind is what you'll get if it's a pointer to any type
> except the special ones listed. I did not try it though, so you should
> confirm. If your struct is going to contain any non-pointers then you
> should check the Kind before checking for Nil to avoid panics.

Jeff Mitchell

unread,
Aug 20, 2012, 5:00:01 PM8/20/12
to Matt Kane's Brain, swh...@ngmoco.com, golan...@googlegroups.com
On Mon, Aug 20, 2012 at 2:23 PM, Matt Kane's Brain <mkb-...@hydrogenproject.com> wrote:
Yup: http://play.golang.org/p/5Squ3mr7K7

On Mon, Aug 20, 2012 at 2:18 PM,  <swh...@ngmoco.com> wrote:
> I think the "Ptr" Kind is what you'll get if it's a pointer to any type
> except the special ones listed.  I did not try it though, so you should
> confirm.  If your struct is going to contain any non-pointers then you
> should check the Kind before checking for Nil to avoid panics.

Thanks for that. I was able to expand it to http://play.golang.org/p/URyXUwpVa_ which serves to verify what I need.

Thanks for all the help, everyone!

Jeff Mitchell

unread,
Aug 20, 2012, 5:21:35 PM8/20/12
to Matt Kane's Brain, swh...@ngmoco.com, golan...@googlegroups.com
For posterity/future questioners' reference, here's my completed function; it compiles, although I have not yet tested it:

func checkNils(strct interface{}) bool {
    v := reflect.ValueOf(strct)
    if v.Kind() != reflect.Struct {
        return false
    }
    for i := 0; i < v.NumField(); i++ {
        if v.Field(i).Kind() != reflect.Ptr {
            return false
        }
        if v.Field(0).Pointer() == 0 {
            return false
        }
    }
    return true
}

roger peppe

unread,
Aug 21, 2012, 3:33:11 AM8/21/12
to Jeff Mitchell, Matt Kane's Brain, swh...@ngmoco.com, golan...@googlegroups.com
On 20 August 2012 22:21, Jeff Mitchell <je...@tomahawk-player.org> wrote:
> For posterity/future questioners' reference, here's my completed function;
> it compiles, although I have not yet tested it:
>
> func checkNils(strct interface{}) bool {
> v := reflect.ValueOf(strct)
> if v.Kind() != reflect.Struct {
> return false
> }
> for i := 0; i < v.NumField(); i++ {
> if v.Field(i).Kind() != reflect.Ptr {
> return false
> }
> if v.Field(0).Pointer() == 0 {
> return false
> }
> }
> return true
> }

I'm not sure that Pointer is the right method to use there,
and I'm sure you don't want to be using Field(0) in the
second expression there :-)

Also returning false when you find a non-pointer doesn't
seem quite right - if you allow non-pointer fields, it allows
a convenient get-out clause for optional fields.

func checkNils(x interface{}) bool {
v := reflect.ValueOf(x)
if v.Kind() != reflect.Struct {
return false
}
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
if f.Kind() != reflect.Ptr {
continue
}
if f.IsNil() {
return false
}
}
return true
}

Jeff Mitchell

unread,
Aug 21, 2012, 7:42:20 AM8/21/12
to roger peppe, Matt Kane's Brain, swh...@ngmoco.com, golan...@googlegroups.com
On Tue, Aug 21, 2012 at 3:33 AM, roger peppe <rogp...@gmail.com> wrote:
On 20 August 2012 22:21, Jeff Mitchell <je...@tomahawk-player.org> wrote:
> For posterity/future questioners' reference, here's my completed function;
> it compiles, although I have not yet tested it:
>
> func checkNils(strct interface{}) bool {
>     v := reflect.ValueOf(strct)
>     if v.Kind() != reflect.Struct {
>         return false
>     }
>     for i := 0; i < v.NumField(); i++ {
>         if v.Field(i).Kind() != reflect.Ptr {
>             return false
>         }
>         if v.Field(0).Pointer() == 0 {
>             return false
>         }
>     }
>     return true
> }

I'm not sure that Pointer is the right method to use there,
and I'm sure you don't want to be using Field(0) in the
second expression there :-)

Heh, yeah, I found that in my actual program pretty quickly. But why isn't Pointer the right method to use? 
 
Also returning false when you find a non-pointer doesn't
seem quite right - if you allow non-pointer fields, it allows
a convenient get-out clause for optional fields.

But I don't allow non-pointer fields. That's the whole point of the JSON unmarshaling paradigm from the blog post -- that your structs are all pointer fields, and you can test whether unmarshaling was successful by checking whether those pointers remain nil.

In my case, I figured that I'd put optional members inside one of the non-optional fields in the struct, as I'm looking for an algorithm for the general case.
 
func checkNils(x interface{}) bool {
        v := reflect.ValueOf(x)
        if v.Kind() != reflect.Struct {
                return false
        }
        for i := 0; i < v.NumField(); i++ {
                f := v.Field(i)
                if f.Kind() != reflect.Ptr {
                        continue
                }
                if f.IsNil() {
                        return false
                }
        }
        return true
}

This looks like a good alternative...so if the field is a pointer and it's nil, IsNil will be true (rather than being true if for some reason the reflect.StructField is nil)?

Thanks,
Jeff

roger peppe

unread,
Aug 21, 2012, 9:27:08 AM8/21/12
to Jeff Mitchell, Matt Kane's Brain, swh...@ngmoco.com, golan...@googlegroups.com
I guess it comes down to whether a function called "checkNils" should
return false because it was passed a struct containing an int.
My feeling was that it probably shouldn't, but YMMV of course.

> In my case, I figured that I'd put optional members inside one of the
> non-optional fields in the struct, as I'm looking for an algorithm for the
> general case.
>
>>
>> func checkNils(x interface{}) bool {
>> v := reflect.ValueOf(x)
>> if v.Kind() != reflect.Struct {
>> return false
>> }
>> for i := 0; i < v.NumField(); i++ {
>> f := v.Field(i)
>> if f.Kind() != reflect.Ptr {
>> continue
>> }
>> if f.IsNil() {
>> return false
>> }
>> }
>> return true
>> }
>
>
> This looks like a good alternative...so if the field is a pointer and it's
> nil, IsNil will be true (rather than being true if for some reason the
> reflect.StructField is nil)?

Semantically there's no difference, I believe, between Value.Pointer() == 0 and
Value.IsNil() but the latter is more conventional (Pointer is generally used
for lower level or debugging stuff).
Reply all
Reply to author
Forward
0 new messages