返回错误的参数太多

adr*_*edx 0 go

我一直在想为什么这段代码会抛出错误:

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
    })
}
Run Code Online (Sandbox Code Playgroud)

如您所见,配对完全正确。每个返回码都返回&ConfigMap并且err和函数签名匹配。我错过了什么?

icz*_*cza 7

您将匿名函数值传递给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
}
Run Code Online (Sandbox Code Playgroud)

您可以做的是创建与 的返回值匹配的全局变量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
}
Run Code Online (Sandbox Code Playgroud)

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

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

如果Config()从多个 goroutine 中调用,则至少有一个会写入全局变量cmand cmErr,并且都会读取它们。所以问这个问题是正确的。

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