2016-08-02 23 views
1

übergeben Ich möchte []map[string]interface{} durch Verweis auf eine Funktion übergeben. Hier ist mein Versuch.Wie durch Verweis ein Stück Karten mit Werttyp Schnittstelle in Golang

package main 

import "fmt" 

func main() { 

    b := map[string]int{"foo": 1, "bar": 2} 
    a := [...]map[string]int{b} 

    fmt.Println(a) 

    editit(a) 

    fmt.Println(a) 
} 

func editit(a interface{}) { 
    fmt.Println(a) 

    b := map[string]int{"foo": 3, "bar": 4} 
    a = [...]map[string]int{b} 

    fmt.Println(a) 
} 

https://play.golang.org/p/9Bt15mvud1

Hier ist ein weiterer Versuch, und was ich will endlich tun. Dies kompiliert nicht.

Antwort

1

Die Funktion übergibt eine **interface{} an Unmarshal. Zum Bestehen der die *[]map[string]interface{} durchAbstellungs, ändern Sie die Funktion Signatur:

func (self BucketStats) GetAuthRequest(rurl string, data interface{}) (err error) { 
2

Mehrere Dinge falsch hier.

Erstens ist [...]map[string]int{b} in Wirklichkeit kein Slice, sondern ein Array fester Länge. Die [...] Syntax bedeutet "mache ein Array und setze die Länge zur Kompilierzeit, basierend darauf, was hineingelegt wird". Die Slice-Syntax lautet einfach []map[string]int{b}. Als Ergebnis führt Ihr Aufruf an editit(a) in der Tat eine Kopie des Arrays, keine Referenz (Scheiben sind von Haus aus Referenzen, Arrays sind nicht). Wenn a in editit() neu zugewiesen wird, weisen Sie die Kopie neu zu, nicht das Original, sodass sich nichts ändert.

Zweitens ist es fast nie sinnvoll, Zeiger auf Schnittstellen zu verwenden. In der Tat wurde die Go-Laufzeit ein paar Versionen geändert, um Zeiger auf Schnittstellen nicht automatisch zu dereferenzieren (wie es für Zeiger auf fast alles andere tut), um diese Gewohnheit zu entmutigen. Schnittstellen sind bereits von Haus aus Referenzen, daher gibt es wenig Grund, einen Zeiger auf einen zu setzen.

Drittens brauchen Sie nicht wirklich brauchen, um einen Verweis auf eine Schnittstelle hier zu übergeben. Sie versuchen, die grundlegende Datenstruktur zu entpacken, die in dieser Schnittstelle enthalten ist. Sie interessieren sich nicht wirklich für die Schnittstelle selbst. GetAuthRequest(rurl string, data interface{}) funktioniert hier gut.

func (self BucketStats) GetSamples() { 

    var buckets []map[string]interface{} 
    self.GetAuthRequest(self.url, &buckets) 

    //ProcessBuckets() 
} 

func (self BucketStats) GetAuthRequest(rurl string, data interface{}) (err error) { 
    client := &http.Client{} 

    req, err := http.NewRequest("GET", rurl, nil) 
    req.SetBasicAuth(self.un, self.pw) 
    resp, err := client.Do(req) 
    if err != nil { 
      return 
    } 

    body, err := ioutil.ReadAll(resp.Body) 
    if err != nil { 
      return 
    } 

    // it's all for this!!! 
    err = json.Unmarshal(body, data) 

    return 
} 

Lassen Sie mich gehen Sie durch, was sich genau nimmt, um:

  • buckets := var buckets []map[string]interface{} Wir brauchen nicht eine Marke hier, weil json.Unmarshal() es für uns füllen.

  • self.GetAuthRequest(self.url, &buckets) Dies übergibt eine Referenz in ein Schnittstellenfeld. Innerhalb GetAuthRequest, data ist eine Schnittstelle mit dem zugrunde liegenden Typ *[]map[string]interface{} und einem zugrunde liegenden Wert gleich der Adresse der ursprünglichen buckets Variable in GetSamples().

  • err = json.Unmarshal(body, data) Dieser übergibt die Schnittstelle data, von Wert ist, an die Schnittstellen Argument json.Unmarshal(). Innerhalb json.Unmarshal(), hat es neue Schnittstelle v mit zugrunde liegenden Typ *[]map[string]interface{} und einen zugrunde liegenden Wert gleich der Adresse der ursprünglichen buckets Variable in GetSamples(). Diese Schnittstelle ist eine andere Variable mit einer anderen Adresse im Speicher von der Schnittstelle, die dieselben Daten in GetAuthRequest enthielt, aber die Daten wurden kopiert, und die Daten enthalten einen Verweis auf Ihre Scheibe, so dass Sie immer noch gut sind.

  • json.Unmarshal() wird durch Reflexion das Segment füllen, auf das die Schnittstelle zeigt, die Sie mit den Daten in Ihrer Anfrage übergeben haben. Es hat einen Verweis auf den Slice-Header buckets, den Sie ihm übergeben haben, obwohl es zwei Schnittstellen durchlaufen hat, um dorthin zu gelangen, so dass alle vorgenommenen Änderungen sich auf die ursprüngliche buckets-Variable auswirken.

Wenn Sie den ganzen Weg zurück bis zu ProcessBuckets(), die buckets Variable werden alle Ihre Daten enthalten.

Als zusätzlichen Vorschlag, verwenden Sie keine benannten Rückgaben, wenn Ihre Funktion mehr als ein paar Zeilen lang ist. Es ist besser, Ihre Variablen explizit zurückzugeben. Dies ist besonders wichtig wegen der variablen Abschattung. Zum Beispiel wird in Ihrer GetAuthRequest() Funktion nie ein Nicht-Null-Fehler zurückgegeben. Dies liegt daran, dass Sie in der Funktionssignatur eine Fehlervariable err deklarieren, die Sie jedoch sofort mit einer lokalen Variablen err überschreiben, indem Sie die Kurzdeklaration in req, err := http.NewRequest("GET", rurl, nil) verwenden. Für den Rest der Funktion bezieht sich err jetzt auf diese neue Fehlervariable und nicht auf die Variable, die als Rückgabevariable definiert ist. Als Ergebnis, wenn Sie zurückkehren, ist die ursprüngliche err Variable in der Rückkehr immernil. Ein viel besserer Stil wäre:

func (self BucketStats) GetAuthRequest(rurl string, **data interface{}) error { 
    client := &http.Client{} 

    req, err := http.NewRequest("GET", rurl, nil) 
    req.SetBasicAuth(self.un, self.pw) 
    resp, err := client.Do(req) 
    if err != nil { 
      return err 
    } 

    body, err := ioutil.ReadAll(resp.Body) 
    if err != nil { 
      return err 
    } 

    // it's all for this!!! 
    return json.Unmarshal(body, data) 
}