我有一个深度嵌套的结构。这些是由json unmarshaller构建的。
然而,这个结构中的一些字段是'omitifempty',所以我结束了一个可以在不同地方使用nill的结构。
示例(真实的东西是更深的嵌套,大而且: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")
}
}
问题是,是否有更通用的方法来测试参考树中的某个节点是否为零?我需要获得很多不同的项目,编写所有这些if语句会很痛苦。 哦,速度是值得关注的;)
答案 0 :(得分:3)
一种处理它的优雅方法(我认为)是将getters添加到用作指针的结构中。生成的protobuf的Go代码还使用了这种“技术”,它允许自然地链接方法调用,而不必担心由于nil
指针引起的运行时恐慌。
在您的示例中,Bar
和Baz
结构用作指针,因此使用吸气剂武装它们。重点是使用指针接收器添加方法,首先必须检查接收器是否为nil
。如果是这样,则返回结果类型的零值。如果不是,请继续返回该结构的字段:
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上尝试。
答案 1 :(得分:1)
如果你想避免' reflect
' (反思,作为"通用"测试任何struct
字段的方式,有点像" Golang - Get pointer to value using reflection"或this gist:它更慢),最可靠的方法是在Foo
上实现方法以返回正确的值
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
}
}
如果要编写很多这样的函数,那么go 1.4 go generate
命令可以帮助生成大部分函数。