英文:
Too many arguments to return 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
})
}
正如你所看到的,配对是完全正确的。每个返回代码都返回&ConfigMap
和err
,函数签名也与之匹配。我漏掉了什么?
英文:
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. What am I missing?
答案1
得分: 8
你将一个匿名函数值传递给了once.Do()
(即Once.Do()
),而return
语句位于其中。这意味着这些return
语句想要从匿名函数中返回,但它没有任何返回值:
func Config() (*ConfigMap, error) {
once.Do(func() {
// 这里不能返回任何值,只能这样写:
return
})
// 在这里你需要返回一些东西:
return &ConfigMap{}, nil
}
你可以创建与Config()
返回值匹配的全局变量,匿名函数应该将这些值存储在其中。在Config()
中,你可以返回这些全局变量的值。
var cm *ConfigMap
var cmErr error
func Config() (*ConfigMap, error) {
once.Do(func() {
// 加载配置,并存储,例如:
cm, cmErr = &ConfigMap{}, nil
})
return cm, cmErr
}
我们真的需要全局变量吗?由于Config()
返回的值是由传递给once.Do()
的匿名函数产生的,而该函数保证只运行一次,所以是的,你需要将它们存储在某个地方,以便能够多次返回它们,即使匿名函数不再被调用/运行(在对Config()
的后续调用中)。
问题:这里可能存在数据竞争吗?
如果从多个goroutine中调用Config()
,至少有一个goroutine将写入全局变量cm
和cmErr
,而所有goroutine都将读取它们。因此,问这个问题是合理的。
但是答案是否定的,上述代码是安全的。全局变量cm
和cmErr
只会被写入一次,并且这发生在它们被读取之前。因为once.Do()
会阻塞,直到匿名函数返回。如果Config()
(因此是once.Do()
)同时从多个goroutine中调用,所有goroutine都将阻塞,直到匿名函数完成(仅一次),并且只有在第一次写入之后才能读取变量。由于匿名函数不会再次运行,因此不会再有写入操作发生。
英文:
You pass an anonymous function value to once.Do()
(which is Once.Do()
), and the return
statements are inside that. Which means those return
statements want to return from the anonymous function, but it doesn't have any return values:
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. And in Config()
you may return the values of these global variables.
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()
).
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. 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. Because once.Do()
blocks until the anonymous function returns. 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. And since the anonymous function will not run anymore, no more writes will happen.
答案2
得分: 1
你在once.Do
的嵌套func()
中调用了return nil, err
等语句。相反,你没有从实际函数中返回。
相反,你可以按照以下方式组织你的代码:
func newConfig() (*Config, error) {
configFilePath, err := filepath.Abs("../build/app.conf")
if err != nil {
return nil, err
}
// 加载 app.conf
cfg, err := ini.Load(configFilePath)
if err != nil {
return nil, err
}
// 获取 app 模式
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
}
// 缓存的配置和任何错误。
var (
cachedConfig *Config
cachedConfigErr error
)
func Config() (*Config, error) {
once.Do(func() {
cachedConfig, cachedConfigErr = newConfig()
})
return cachedConfig, cachedConfigErr
}
英文:
You're calling return nil, err
and similar from the nested func()
inside your 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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论