在編寫go程序時,經常會遇到關于指針和內存地址的問題。尤其是在使用第三方庫如viper時,指針的使用更加復雜。本文將通過一個具體的例子,詳細解釋在go語言中使用viper庫時的指針傳參問題。
首先,我們來看一下代碼示例:
setting模塊:
type setting struct { vp *viper.viper } func newsetting() (*setting, error) { vp := viper.new() vp.setconfigname("config") vp.addconfigpath("configs/") vp.setconfigtype("yaml") err := vp.readinconfig() if err != nil { return nil, err } return &setting{vp: vp}, nil }
section模塊:
立即學習“go語言免費學習筆記(深入)”;
type serversettings struct { runmode string httpport string readtimeout time.duration writetimeout time.duration } func (s *setting) readsection(k string, v interface{}) error { err := s.vp.unmarshalkey(k, v) if err != nil { return err } return nil }
global模塊:
var serversetting *setting.serversettings
main模塊:
setting, err := setting.newsetting() setting.readsection("server", &global.serversetting)
在上述代碼中,如果我們在main模塊中將第二行修改為setting.readsection(“server”, global.serversetting),程序會報錯result must be addressable (a pointer)。為什么會出現這個問題呢?
首先需要明確的是,雖然global.serversetting是一個指針,但這并不意味著它可以被直接傳遞給readsection函數。我們需要傳遞的是指針的地址,而不是指針本身。
要理解這一點,我們需要深入viper庫的源碼。在viper庫的newdecoder函數中,有這樣一段代碼:
// newdecoder returns a new decoder for the given configuration. once // a decoder has been returned, the same configuration must not be used // again. func newdecoder(config *decoderconfig) (*decoder, error) { val := reflect.valueof(config.result) if val.kind() != reflect.ptr { return nil, errors.new("result must be a pointer") } val = val.elem() if !val.canaddr() { return nil, errors.new("result must be addressable (a pointer)") } }
這段代碼說明了問題的關鍵:傳遞的參數不僅需要是一個指針,還必須是可以被尋址的(addressable)。當我們傳遞一個結構體的指針時,它并不能被尋址,因此會報錯。
為了進一步理解這個問題,我們可以看一個簡單的例子:
package main import ( "fmt" "reflect" ) var a *db type db struct { } func main() { val := reflect.valueof(a) val = val.elem() fmt.println(val.canaddr()) val = reflect.valueof(&a) val = val.elem() fmt.println(val.canaddr()) }
這段代碼的輸出結果是:
false true
這表明,當我們直接傳遞a時,它是不可尋址的;而當我們傳遞&a時,它是可以被尋址的。
因此,在使用viper庫時,我們需要傳遞指針的地址,而不是指針本身。這就是為什么在main模塊中,我們需要使用&global.serversetting而不是global.serversetting。
通過這個例子,我們可以更好地理解go語言中指針的使用以及viper庫的具體要求。希望這能幫助你在編寫go程序時更好地處理指針和內存地址的問題。