本文转载请联系Golang技术分享公众号。在调试Go程序的时候,我们常常想知道对象的内部数据是什么样子的,从而掌握程序的运行状态。一般有两种方法:对于简单的代码测试,我们可以通过fmt包打印一些对象信息;在稍微复杂的场景下,我们可以使用调试器,比如GDB、LLDB、Delve。然而,这两种方法都有缺点。fmt包可以打印的信息并不友好,尤其是当结构包含指针对象时;通过调试器调试程序往往受到各种因素的限制,例如远程访问服务器。例子对于fmt包的不足,我们来看一个例子。定义instance和Inner结构,其中instance的C属性字段是一个Inner类型指针。typeinstancestruct{AstringBintC*Inner}typeInnerstruct{DstringEstring}实例化一个实例对象insfuncmain(){ins:=instance{A:"AAAA",B:1000,C:&Inner{D:"DDDD",E:"EEEE",},}fmt.Println(ins)}此时,我们想知道ins的内部数据。fmt.Println(ins)语句得到的打印信息如下{AAAA10000xc000054020}由于C字段是一个指针,所以打印出来的地址是0xc000054020,但是隐藏了地址后面的数据。显然,这对程序排错是很不友好的。go-spewgo-spew就是为解决上述问题而诞生的,它实现了Go数据结构的深度打印机。同样以上面的代码为例,这次使用go-spew来打印。下载goget-ugithub.com/davecgh/go-spew/spew引导包"github.com/davecgh/go-spew/spew"printfuncmain(){ins:=instance{A:"AAAA",B:1000,C:&Inner{D:"DDDD",E:"EEEE",},}spew.Dump(ins)}得到打印结果(main.instance){A:(string)(len=4)"AAAA",B:(int)1000,C:(*main.Inner)(0xc0000ba0c0)({D:(string)(len=4)"DDDD",E:(string)(len=4)"EEEE"})}是很详细?场景扩展指针数组不够清晰,无法打印fmt,除非结构包含指针对象。如果数组或者map中有指针对象,传统的打印也是不友好的。typeDemostruct{aintbstring}funcmain(){arr:=[...]*Demo{{100,"Python"},{200,"Golang"}}fmt.Printf("%v\n----------------分隔线------------\n",arr)spew.Dump(arr)}两次打印输出结果对比[0xc00011c0180xc00011c030]-----------------分隔线------------([2]*main.Demo)(len=2cap=2){(*main.演示)(0xc00011c018)({a:(int)100,b:(string)(len=6)"Python"}),(*main.Demo)(0xc00011c030)({a:(int)200,b:(string)(len=6)"Golang"})}哪个强哪个弱一目了然。循环结构可以通过spew.Dump方法打印指针地址和它指向的数据。如果go-spew需要打印循环数据结构怎么办?能否正确处理(而不是陷入死循环)?定义循环结构对象CirculartypeCircularstruct{aintnext*Circular}实例化了循环结构对象,然后分别通过fmt和go-spew进行打印比较。funcmain(){c:=&Circular{1,nil}c.next=&Circular{2,c}fmt.printf("%+v\n----------------分割线------------------\n",c)spew.Dump(c)}得到结果&{a:1next:0xc0000962f0}----------------分割线-----------------(*main.Circular)(0xc0000962e0)({a:(int)1,next:(*main.Circular)(0xc0000962f0)({a:(int)2,next:(*main.Circular)(0xc0000962e0)(
