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模块

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传参的时候必须取这个指针的地址呢

阅读 3.5k
3 个回答

在 viper 源码中写到了

// 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)")
    }

所以问题的关键是,你传递的参数不仅仅要求是一个指针,并且它可以被寻址,也就是 can be 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

那至于什么可以被寻址,什么不可以被寻址,已经为什么指针不能被寻址,可以参考下面的链接:
go-addressable
GoAddressableValues

var ServerSetting   *setting.ServerSettingS

这行实际上你定义了一个指针类型的变量且没有显式初始化,它的值是 nil, 所以你不对该变量取地址,传参就相当于传了nil给 setting.ReadSection 函数。

如果你这么写就可以解决你的问题

var ServerSetting *setting.ServerSettingS = &setting.ServerSettingS{}
新手上路,请多包涵
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题