我有一个嵌套的结构。这些是由json解组器构造的。
但是,此结构中的某些字段是’omitifempty’的,因此我以一个可以在各个位置包含nill的结构结束op。
示例(真正的东西嵌套得更深,很大:400行结构):
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") } }
问题是,是否存在一种更通用的方法来测试参考树中的某个节点是否为nil?我需要获得很多不同的项目,而编写所有这些if语句将很痛苦。哦,速度值得关注。
一种优雅的处理方式(在我看来)是将getters添加到用作指针的结构中。protobuf的生成的Go代码也使用此“技术” ,它允许方法调用自然链接,而不必担心由于nil指针导致的运行时恐慌。
nil
在您的示例中,Barand Baz结构用作指针,因此请使用getter武装它们。重点是使用指针接收器添加方法,首先必须检查接收器是否为nil。如果是这样,则返回结果类型的零值。如果不是,则继续返回该结构的字段:
Bar
Baz
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 }
关于使用指针接收器的方法的好处是,您可以使用nil接收器来调用它们。在您尝试引用它们的字段之前,它不会引起运行时恐慌,这就是为什么我们首先要检查接收器是否为nil(最终,接收器充当普通参数,并且nil作为指针传递也绝不是错误。论据)。
具有上述吸气剂后,使用情况将得到简化,并且在以下任何示例中都不会发生运行时恐慌:
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") }
在Go Playground上尝试一下。