2017-02-06 8 views
0

Ich habe meine Haare zu ziehen ist, warum dieser Code führt den Fehler:Zu viele Argumente zurück Fehler

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

Wie Sie sehen können, die Paarung genau richtig ist. Jeder Rückgabecode gibt &ConfigMap und err zurück und die Funktionssignatur stimmt damit überein. Was vermisse ich?

Antwort

3

Sie übergeben einen anonymen Funktionswert an once.Do() (das ist Once.Do()), und die return Anweisungen sind darin enthalten. Was bedeutet diese return Aussagen der anonymen Funktion zurückkehren wollen, aber es keine Rückgabewerte hat:

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 
} 

Was Sie tun können, ist den Rückgabewert von Config() und die anonymen Funktion sollte passende globale Variablen erstellen Speichern Sie die Werte in ihnen. Und in Config() können Sie die Werte dieser globalen Variablen zurückgeben.

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 
} 

Brauchen wir wirklich globale Variablen? Da die von Config() zurückgegebenen Werte von der an once.Do() übergebenen anonymen Funktion erzeugt werden, die garantiert nur einmal ausgeführt wird, ja, müssen Sie sie irgendwo speichern, um sie mehrfach zurückgeben zu können, selbst wenn die anonyme Funktion nicht aufgerufen wird/run mehr (bei nachfolgenden Anrufen an Config()).

Frage: Kann es hier ein Datenrennen geben?

Wenn Config() von mehreren goroutines genannt wird, zumindest wird man die globalen Variablen schreiben cm und cmErr, und alle sie lesen werden. Es ist also recht, diese Frage zu stellen.

Aber die Antwort ist nein, der obige Code ist sicher. Die globalen Variablen cm und cmErr werden nur einmal geschrieben, und dies geschieht, bevor sie gelesen werden konnten. Weil once.Do() blockiert, bis die anonyme Funktion zurückkehrt. Wenn Config() (und damit once.Do()) gleichzeitig von mehreren goroutines aufgerufen wird, werden alle blockieren, bis die anonyme Funktion (nur einmal) abgeschlossen ist, und das Lesen der Variablen kann erst nach dem ersten Schreibvorgang erfolgen. Und da die anonyme Funktion nicht mehr läuft, werden keine Schreibvorgänge mehr stattfinden.

1

Sie rufen return nil, err und ähnliche aus dem verschachtelten func() in Ihrem once.Do. Umgekehrt kehren Sie nicht von der eigentlichen Funktion zurück.

Stattdessen können Sie Ihren Code wie folgt strukturiert:

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 
} 
Verwandte Themen