返回错误的参数太多

时间:2017-02-06 14:00:44

标签: go

我一直在拉我的头发,为什么这段代码会抛出错误:

max-height

如您所见,配对完全正确。每个返回码返回package util import ( "path/filepath" "sync" "github.com/go-ini/ini" ) // ConfigMap is map for config values type ConfigMap struct { LogPath string PublicDir string SessionName string Debug bool DBUsersHost string DBUsersName string DBUsersUsername string DBUsersPassword string } var once sync.Once // Config loads and return config object func Config() (*ConfigMap, error) { once.Do(func() { // Find the location of the app.conf file configFilePath, err := filepath.Abs("../build/app.conf") if err != nil { return nil, err } // Load app.conf cfg, err := ini.Load(configFilePath) if err != nil { return nil, err } // Get app mode mode, err := AppMode() if err != nil { return nil, err } c := &ConfigMap{} err = cfg.Section(mode).MapTo(c) if err != nil { return nil, err } return c, err }) } &ConfigMap,函数签名与之匹配。我错过了什么?

2 个答案:

答案 0 :(得分:3)

您将匿名函数值传递给once.Do()Once.Do()),return语句位于其中。这意味着那些return语句想要从匿名函数返回,但它没有任何返回值:

func Config() (*ConfigMap, error) {
    once.Do(func() {
        // You can't return any values here, only this works:
        return
    })

    // And you do need to return something here:
    return &ConfigMap{}, nil
}

您可能要做的是创建与Config()的返回值匹配的全局变量,并且匿名函数应该将值存储在其中。在Config()中,您可以返回这些全局变量的值。

var cm *ConfigMap
var cmErr error

func Config() (*ConfigMap, error) {
    once.Do(func() {
        // load config, and store, e.g.
        cm, cmErr = &ConfigMap{}, nil
    })

    return cm, cmErr
}

我们真的需要全局变量吗?由于Config()返回的值是由传递给once.Do()的匿名函数产生的,保证只运行一次,是的,你需要将它们存储在某个地方以便能够多次返回它们,即使是匿名函数不再被调用/运行(在后续调用Config()时)。

问题:这里可能有数据竞争吗?

如果从多个goroutine调用Config(),则至少有一个将编写全局变量cmcmErr,并且所有变量都将读取它们。所以提出这个问题是合法的。

但答案是否定的,上面的代码是安全的。全局变量cmcmErr只写一次,这在它们被读取之前发生。因为once.Do()会阻塞,直到匿名函数返回。如果从多个goroutine同时调用Config()(因而once.Do()),则所有将阻塞直到匿名函数完成(仅一次),并且只有在第一次写入后才能读取变量。由于匿名函数不再运行,因此不会再发生写入。

答案 1 :(得分:1)

您在return nil, err内的嵌套func()中呼叫once.Do和类似内容。相反,您不会从实际功能返回。

相反,您可以像这样构建代码:

func newConfig() (*Config, error) {
    configFilePath, err := filepath.Abs("../build/app.conf")
    if err != nil {
        return nil, err
    }

    // Load app.conf
    cfg, err := ini.Load(configFilePath)
    if err != nil {
        return nil, err
    }

    // Get app mode
    mode, err := AppMode()
    if err != nil {
        return nil, err
    }

    c := &ConfigMap{}
    err = cfg.Section(mode).MapTo(c)
    if err != nil {
        return nil, err
    }

    return c, err
}

// Cached config and any error.
var (
   cachedConfig *Config
   cachedConfigErr error
)

func Config() (*Config, error) {
  once.Do(func() {
    cachedConfig, cachedConfigErr = newConfig()
  })
  return cachedConfig, cachedConfigErr
}