您现在的位置是:亿华云 > 应用开发

看透 Go 对象内部细节的神器

亿华云2025-10-03 18:14:15【应用开发】4人已围观

简介本文转载自微信公众号「Golang技术分享」,作者机器铃砍菜刀。转载本文请联系Golang技术分享公众号。在调式 Go 程序时,我们经常想知道对象的内部数据是什么样了,以便掌握程序的运行情况。一般有两

本文转载自微信公众号「Golang技术分享」,看透作者机器铃砍菜刀。对象的神转载本文请联系Golang技术分享公众号。内部

在调式 Go 程序时,细节我们经常想知道对象的看透内部数据是什么样了,以便掌握程序的对象的神运行情况。

一般有两种做法:对于简单的内部代码测试,我们可以通过fmt包来打印一些对象信息;在稍复杂场景下,细节可以利用调式器来完成,看透例如 GDB、对象的神LLDB 和 Delve 等。内部

但是细节,这两种做法都有不足之处。看透fmt包能打印的对象的神信息并不友好,尤其在结构体中含有指针对象时;通过调式器来调式程序也经常受限于各种因素,内部例如远程访问服务器。

示例

对于 fmt 包的能力短板,我们来看一个例子。

定义 instance 和 Inner 结构体,站群服务器其中 instance 的C属性字段是 Inner 类型指针。

type instance struct {   A string  B int  C *Inner } type Inner struct {   D string  E string } 

实例化一个 instance 对象ins

func main() {   ins := instance{    A: "AAAA",   B: 1000,   C: &Inner{     D: "DDDD",    E: "EEEE",   },  }  fmt.Println(ins) } 

此时,我们想知道ins的内部数据。通过fmt.Println(ins)语句得到的打印信息如下

{ AAAA 1000 0xc000054020} 

由于 C 字段是指针,所以打印出来的是一个地址0xc000054020,而地址背后的数据却被隐藏了。显然,这对程序排查非常不友好。

go-spew

go-spew 就是为了解决上述问题而生的,它为 Go 数据结构实现了一个深度打印机。

同样以上文代码为例,这次使用 go-spew 进行打印。

下载

go get -u github.com/davecgh/go-spew/spew 

导包

"github.com/davecgh/go-spew/spew" 

打印

func main() {   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中是指针对象时,传统的打印同样不友好。高防服务器

type Demo struct {   a int  b string } func main() {   arr := [...]*Demo{ { 100, "Python"}, { 200, "Golang"}}  fmt.Printf("%v\n-----------------分割线-----------\n", arr)  spew.Dump(arr) } 

两种打印的输出结果对比

[0xc00011c018 0xc00011c030] -----------------分割线----------- ([2]*main.Demo) (len=2 cap=2) {   (*main.Demo)(0xc00011c018)({    a: (int) 100,   b: (string) (len=6) "Python"  }),  (*main.Demo)(0xc00011c030)({    a: (int) 200,   b: (string) (len=6) "Golang"  }) } 

孰强孰弱,一目了然。

循环结构

通过 spew.Dump 方法可以将指针地址和它指向的数据都打印出来,那如果 go-spew 需要打印循环数据结构怎么办,它能否正确处理(而不是陷入无限循环)?

定义循环结构体对象 Circular

type Circular struct {   a    int  next *Circular } 

实例化循环结构体对象,再分别通过 fmt 和 go-spew 进行打印对比

func main() {   c := &Circular{ 1, nil}  c.next = &Circular{ 2, c}  fmt.Printf("%+v\n----------------分割线-------------------\n", c)  spew.Dump(c) } 

得到结果

&{ a:1 next:0xc0000962f0} ----------------分割线------------------- (*main.Circular)(0xc0000962e0)({   a: (int) 1,  next: (*main.Circular)(0xc0000962f0)({    a: (int) 2,   next: (*main.Circular)(0xc0000962e0)(<already shown>)  }) }) 

再次证明 go-spew 的强大。

总结

go-spew 借助于 unsafe 包,为我们带来了非常漂亮的打印功能。

当然,go-spew 不止 Dump 方法,它也提供了其他方法,例如转换为字符串的 Sdump 方法;输出重定向的 Fdump 方法;与 fmt 类似的一套 Print 用法。

同时,可以通过 spew.Config 进行一些参数配置,例如设置 spew.Config.MaxDepth 用于控制打印深度。

调式 Go 程序时,go-spew 是一个非常好用的助手工具,推荐大家使用。

源码下载

很赞哦!(2)