How can I check nested pointer easily?
type inner1 struct {
value string
}
type inner2 struct {
value *inner1
}
type outter struct {
value *inner2
}
I have data like this:
o := &outter{
value: &inner2{
value: &inner1{
value: "I need this data",
},
},
}
If I want to get data from this, I need to check for nil pointer.
func printValue(o *outter) (string, bool) {
if o.value != nil {
v1 := o.value
if v1.value != nil {
v2 := v1.value
return v2.value, true
}
}
return "", false
}
Yea, I can do this. But in my real scenario, this nested pointer part is much longer. I will prefer another way.
I have checked this answer, Test for nil values in golang nested stucts. But I need alternative.
How can I accomplish this. Any efficient and better option?
Panic and recover is for exceptional cases. Checking if a pointer is nil
is usually not, so you should stay away from it. Checking if a pointer is nil
using an if
statement is much cleaner than to introduce a deferred function with recover()
. Especially as this recover()
will stop all other kinds of panic, even those that would result from other reasons than trying to dereference a nil
pointer. (Using defer
and recover()
is also slower than a simple nil
check using if
.)
If the data is always required to be a non-nil
value, you should consider not using a pointer for it. And if the data is always required to be non-nil
but for some reason you are required to use a pointer, then this may be a valid case to let your code panic if any of the pointers are still nil
.
If it may be nil
, then you have to check the nil
case and handle it appropriately. You have to be explicit about this, Go doesn't help you omit this check.
To avoid having to check nil
in every place, a utility function or method is reasonable. Note that methods can be called even if the receiver is nil
which may be useful in such cases.
For example you may attach the following methods:
func (i *inner1) Get() (string, bool) {
if i == nil {
return "", false
}
return i.value, true
}
func (i *inner2) Get() (string, bool) {
if i == nil {
return "", false
}
return i.value.Get()
}
func (o *outter) Get() (string, bool) {
if o == nil {
return "", false
}
return o.value.Get()
}
Note that each Get()
method requires to check a single pointer, doesn't matter how complex the data structure is.
Testing it:
o := &outter{
value: &inner2{
value: &inner1{
value: "I need this data",
},
},
}
fmt.Println(o.Get())
o.value.value = nil
fmt.Println(o.Get())
o.value = nil
fmt.Println(o.Get())
o = nil
fmt.Println(o.Get())
Output (try it on the Go Playground):
I need this data true
false
false
false
The above solution hides the internals of outter
which is useful for those using outter
(doesn't need updating the clients if internals of outter
change, just the outter.Get()
method).
A similar approach would be to add methods that only return the field of the receiver struct:
func (i *inner1) Value() (string, bool) {
if i == nil {
return "", false
}
return i.value, true
}
func (i *inner2) Inner1() *inner1 {
if i == nil {
return nil
}
return i.value
}
func (o *outter) Inner2() *inner2 {
if o == nil {
return nil
}
return o.value
}
This approach requires clients to know internals of outter
, but similarly it does not require any nil
checks when using it:
o := &outter{
value: &inner2{
value: &inner1{
value: "I need this data",
},
},
}
fmt.Println(o.Inner2().Inner1().Value())
o.value.value = nil
fmt.Println(o.Inner2().Inner1().Value())
o.value = nil
fmt.Println(o.Inner2().Inner1().Value())
o = nil
fmt.Println(o.Inner2().Inner1().Value())
Output is the same. Try this one on the Go Playground.