简体   繁体   English

参数过多,无法返回错误

[英]Too many arguments to return error

I've been pulling my hair out as to why this code throws the error: 我一直在努力解释为什么此代码引发错误:

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

As you can see, the pairing is exactly correct. 如您所见,配对完全正确。 Each return code returns &ConfigMap and err and the function signature matches it. 每个返回代码都返回&ConfigMaperr ,并且函数签名与之匹配。 What am I missing? 我想念什么?

You pass an anonymous function value to once.Do() (which is Once.Do() ), and the return statements are inside that. 你传递一个匿名函数值once.Do()这是Once.Do()return语句是内部的。 Which means those return statements want to return from the anonymous function, but it doesn't have any return values: 这意味着这些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
}

What you may do is create global variables matching the return values of Config() , and the anonymous function should store the values in them. 您可能要做的是创建与Config()的返回值匹配的全局变量,并且匿名函数应将值存储在其中。 And in Config() you may return the values of these global variables. 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
}

Do we really need global variables? 我们真的需要全局变量吗? Since the values returned by Config() are produced by the anonymous function passed to once.Do() which is guaranteed to run only once, yes, you need to store them somewhere to be able to return them multiple times, even when the anonymous function is not called / run anymore (on subsequent calls to Config() ). 由于Config()返回的值是由传递给一次的匿名函数产生的, once.Do()保证只能运行一次,因此,是的,您需要将它们存储在某个位置以能够多次返回,即使匿名函数不再被调用/运行(在对Config()后续调用中)。

Question: May there be a data race here? 问题:这里可能会进行数据竞赛吗?

If Config() is called from multiple goroutines, at least one will write the global variables cm and cmErr , and all will read them. 如果从多个goroutine中调用Config()则至少要写入一个全局变量cmcmErr ,所有这些都将读取它们。 So it's rightful to ask this question. 所以问这个问题是正确的。

But the answer is no, the above code is safe. 但是答案是否定的,上面的代码是安全的。 The global variables cm and cmErr are only written once, and this happens before they could be read. 全局变量cmcmErr仅写入一次,这在可以读取之前发生。 Because once.Do() blocks until the anonymous function returns. 因为一次once.Do()阻塞,直到匿名函数返回为止。 If Config() (and thus once.Do() ) is called simultaneously from multiple goroutines, all will block until the anonymous function completes (once only), and reading the variables can happen only after the first write. 如果Config()因此once.Do()同时从多个够程叫,都将阻塞,直到匿名函数完成(一次),并读取变量只能在第一次写入之后发生。 And since the anonymous function will not run anymore, no more writes will happen. 并且由于匿名函数将不再运行,因此不会再进行写操作。

You're calling return nil, err and similar from the nested func() inside your once.Do . 你打电话return nil, err和嵌套类似func()的内部once.Do Conversely, you're not returning from the actual function. 相反,您不是从实际函数中返回。

Instead, you can structure your code like this: 相反,您可以像下面这样构造代码:

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM