I have a deeply nested struct in go. These are constructed by a json unmarshaller.
Quite some fields in this struct are however 'omitifempty' so I end op with a struct that can have nills in various places.
Example (the real thing is even deeper nested, and big: 400 lines of structs):
package main
import "fmt"
type Foo struct {
Foo string
Bar *Bar
}
type Bar struct {
Bar string
Baz *Baz
}
type Baz struct {
Baz string
}
func main() {
f1 := Foo{Foo: "f1"}
f2 := Foo{Foo: "f2", Bar: &Bar{Bar: "br2"}}
f3 := Foo{Foo: "f3", Bar: &Bar{Bar: "br3", Baz: &Baz{Baz: "bz3"}}}
fmt.Println(f3.Bar.Baz.Baz) //-> bz3
fmt.Println(f2.Bar.Baz.Baz) //-> panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(f1.Bar.Baz.Baz) //-> panic: runtime error: invalid memory address or nil pointer dereference
//so far so good, but
//is there a more generic way to do this kind of testing?
if f2.Bar != nil && f2.Bar.Baz != nil {
fmt.Println(f2.Bar.Baz.Baz)
} else {
fmt.Println("something nil")
}
}
The question is if there is a more generic way to test if some node in the reference tree is nil? I need to get a lot of different items and it will be a pain to write all these if statements. Oh and speed is of concern.
One elegant way (in my opinion) of handling it is to add getters to structs that are used as pointers. This "technique" is also used by the generated Go code of protobuf, and it allows natural chaining of method calls without having to worry about runtime panic due to nil
pointers.
In your example the Bar
and Baz
structs are used as pointers, so arm them with getters. The focus is on adding methods with pointer receiver, and first it must be checked if the receiver is nil
. If so, return the zero value of the result type. If not, proceed to return the field of the struct:
func (b *Bar) GetBaz() *Baz {
if b == nil {
return nil
}
return b.Baz
}
func (b *Baz) GetBaz() string {
if b == nil {
return ""
}
return b.Baz
}
The good thing about methods with pointer receivers is that you may call them with nil
receivers. It does not cause a runtime panic until you try to refer to their fields, which we don't, that's why we first check if the receiver is nil
(ultimately, receivers act as normal parameters–and it's never an error to pass nil
as a pointer argument).
Having the above getters, use is simplified to this, and no runtime panic occurs in any of these examples:
fmt.Println(f3.Bar.GetBaz().GetBaz()) // naturally no panic
fmt.Println(f2.Bar.GetBaz().GetBaz()) // No panic
fmt.Println(f1.Bar.GetBaz().GetBaz()) // No panic
if baz := f2.Bar.GetBaz(); baz != nil {
fmt.Println(baz.GetBaz())
} else {
fmt.Println("something nil")
}
Try it on the Go Playground.
If you want to avoid 'reflect
' (reflection, as a "generic" way to test fields of any struct
, a bit as in "Get pointer to value using reflection" or in this gist: it is slower), the surest way would be to implement methods on Foo
in order to return the right value
func (foo *Foo) BarBaz() string {
if f2.Bar != nil && f2.Bar.Baz != nil {
return f2.Bar.Baz.Baz
} else {
fmt.Println("something nil")
return "" // for example
}
}
If there are a lot of such functions to write, maybe go 1.4 go generate
command can help generate most of them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With