看透 Go 对象内部细节的神器
在調(diào)式 Go 程序時(shí),我們經(jīng)常想知道對(duì)象的內(nèi)部數(shù)據(jù)是什么樣了,以便掌握程序的運(yùn)行情況。
一般有兩種做法:對(duì)于簡(jiǎn)單的代碼測(cè)試,我們可以通過(guò)fmt包來(lái)打印一些對(duì)象信息;在稍復(fù)雜場(chǎng)景下,可以利用調(diào)式器來(lái)完成,例如 GDB、LLDB 和 Delve 等。
但是,這兩種做法都有不足之處。fmt包能打印的信息并不友好,尤其在結(jié)構(gòu)體中含有指針對(duì)象時(shí);通過(guò)調(diào)式器來(lái)調(diào)式程序也經(jīng)常受限于各種因素,例如遠(yuǎn)程訪問(wèn)服務(wù)器。
示例
對(duì)于 fmt 包的能力短板,我們來(lái)看一個(gè)例子。
定義 Instance 和 Inner 結(jié)構(gòu)體,其中 Instance 的C屬性字段是 Inner 類型指針。
type?Instance?struct?{A?stringB?intC?*Inner }type?Inner?struct?{D?stringE?string }實(shí)例化一個(gè) Instance 對(duì)象ins
func?main()?{ins?:=?Instance{A:?"AAAA",B:?1000,C:?&Inner{D:?"DDDD",E:?"EEEE",},}fmt.Println(ins) }此時(shí),我們想知道ins的內(nèi)部數(shù)據(jù)。通過(guò)fmt.Println(ins)語(yǔ)句得到的打印信息如下
{AAAA?1000?0xc000054020}由于 C 字段是指針,所以打印出來(lái)的是一個(gè)地址0xc000054020,而地址背后的數(shù)據(jù)卻被隱藏了。顯然,這對(duì)程序排查非常不友好。
go-spew
go-spew 就是為了解決上述問(wèn)題而生的,它為 Go 數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)了一個(gè)深度打印機(jī)。
同樣以上文代碼為例,這次使用 go-spew 進(jìn)行打印。
下載
go?get?-u?github.com/davecgh/go-spew/spew導(dǎo)包
"github.com/davecgh/go-spew/spew"打印
func?main()?{ins?:=?Instance{A:?"AAAA",B:?1000,C:?&Inner{D:?"DDDD",E:?"EEEE",},}spew.Dump(ins) }得到打印結(jié)果
(main.Instance)?{A:?(string)?(len=4)?"AAAA",B:?(int)?1000,C:?(*main.Inner)(0xc0000ba0c0)({D:?(string)?(len=4)?"DDDD",E:?(string)?(len=4)?"EEEE"}) }是不是非常詳細(xì)?
場(chǎng)景擴(kuò)展
指針數(shù)組
除了結(jié)構(gòu)體中含有指針對(duì)象時(shí)打印 fmt 打印不夠清晰,如果數(shù)組或者map中是指針對(duì)象時(shí),傳統(tǒng)的打印同樣不友好。
type?Demo?struct?{a?intb?string }func?main()?{arr?:=?[...]*Demo{{100,?"Python"},?{200,?"Golang"}}fmt.Printf("%v\n-----------------分割線-----------\n",?arr)spew.Dump(arr) }兩種打印的輸出結(jié)果對(duì)比
[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"}) }孰強(qiáng)孰弱,一目了然。
循環(huán)結(jié)構(gòu)
通過(guò) spew.Dump 方法可以將指針地址和它指向的數(shù)據(jù)都打印出來(lái),那如果 go-spew 需要打印循環(huán)數(shù)據(jù)結(jié)構(gòu)怎么辦,它能否正確處理(而不是陷入無(wú)限循環(huán))?
定義循環(huán)結(jié)構(gòu)體對(duì)象 Circular
type?Circular?struct?{a????intnext?*Circular }實(shí)例化循環(huán)結(jié)構(gòu)體對(duì)象,再分別通過(guò) fmt 和 go-spew 進(jìn)行打印對(duì)比
func?main()?{c?:=?&Circular{1,?nil}c.next?=?&Circular{2,?c}fmt.Printf("%+v\n----------------分割線-------------------\n",?c)spew.Dump(c) }得到結(jié)果
&{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 的強(qiáng)大。
總結(jié)
go-spew 借助于 unsafe 包,為我們帶來(lái)了非常漂亮的打印功能。
當(dāng)然,go-spew 不止 Dump 方法,它也提供了其他方法,例如轉(zhuǎn)換為字符串的 Sdump 方法;輸出重定向的 Fdump 方法;與 fmt 類似的一套 Print 用法。
同時(shí),可以通過(guò) spew.Config 進(jìn)行一些參數(shù)配置,例如設(shè)置 spew.Config.MaxDepth 用于控制打印深度。
調(diào)式 Go 程序時(shí),go-spew 是一個(gè)非常好用的助手工具,推薦大家使用。
感謝你的點(diǎn)贊和在看哦~
總結(jié)
以上是生活随笔為你收集整理的看透 Go 对象内部细节的神器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何有效控制 Go 线程数?
- 下一篇: MySQL 的 bug 必须修复吗?