Daten (Typschnittstelle {}) können nicht in Typzeichenfolge konvertiert werden: Typzusicherung erforderlich

176

Ich bin ziemlich neu und habe mit diesem Benachrichtigungspaket gespielt .

Zuerst hatte ich Code, der so aussah:

func doit(w http.ResponseWriter, r *http.Request) {
    notify.Post("my_event", "Hello World!")
    fmt.Fprint(w, "+OK")
}

Ich wollte Newline anhängen, Hello World!aber nicht in der doitobigen Funktion , weil das ziemlich trivial wäre, aber im handlerFolgenden wie folgt :

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    fmt.Fprint(w, data + "\n")
}

Nachher go run:

$ go run lp.go 
# command-line-arguments
./lp.go:15: invalid operation: data + "\n" (mismatched types interface {} and string)

Nach ein bisschen googeln fand ich diese Frage auf SO .

Dann habe ich meinen Code aktualisiert auf:

func handler(w http.ResponseWriter, r *http.Request) {
    myEventChan := make(chan interface{})
    notify.Start("my_event", myEventChan)
    data := <-myEventChan
    s:= data.(string) + "\n"
    fmt.Fprint(w, s)
}

Sollte ich das tun? Meine Compilerfehler sind verschwunden, also denke ich, dass das ziemlich gut ist? Ist das effizient? Solltest du es anders machen?

Alfred
quelle

Antworten:

291

Gemäß der Go-Spezifikation :

Für einen Ausdruck x vom Schnittstellentyp und einen Typ T behauptet der primäre Ausdruck x (T), dass x nicht Null ist und dass der in x gespeicherte Wert vom Typ T ist.

Mit einer "Typzusicherung" können Sie deklarieren, dass ein Schnittstellenwert einen bestimmten konkreten Typ enthält oder dass sein konkreter Typ eine andere Schnittstelle erfüllt.

In Ihrem Beispiel haben Sie behauptet, dass Daten (Typschnittstelle {}) die konkrete Typzeichenfolge haben. Wenn Sie sich irren, gerät das Programm zur Laufzeit in Panik. Sie müssen sich keine Gedanken über die Effizienz machen. Für die Überprüfung müssen lediglich zwei Zeigerwerte verglichen werden.

Wenn Sie sich nicht sicher sind, ob es sich um eine Zeichenfolge handelt oder nicht, können Sie die Syntax mit zwei Rückgaben testen.

str, ok := data.(string)

Wenn Daten keine Zeichenfolge sind, ist ok falsch. Es ist dann üblich, eine solche Anweisung in eine if-Anweisung wie folgt zu verpacken:

if str, ok := data.(string); ok {
    /* act on str */
} else {
    /* not string */
}
Stephen Weinberg
quelle
29

Typ Assertion

Dies ist bekannt als type assertionin Golang, und es ist eine übliche Praxis.

Hier ist die Erklärung von einer Tour von go :

Eine Typzusicherung bietet Zugriff auf den zugrunde liegenden konkreten Wert eines Schnittstellenwerts.

t := i.(T)

Diese Anweisung behauptet, dass der Schnittstellenwert i den konkreten Typ T enthält und weist der Variablen t den zugrunde liegenden T-Wert zu.

Wenn ich kein T halte, löst die Anweisung eine Panik aus.

Um zu testen, ob ein Schnittstellenwert einen bestimmten Typ enthält, kann eine Typzusicherung zwei Werte zurückgeben: den zugrunde liegenden Wert und einen booleschen Wert, der angibt, ob die Zusicherung erfolgreich war.

t, ok := i.(T)

Wenn ich ein T habe, ist t der zugrunde liegende Wert und ok ist wahr.

Wenn nicht, ist ok falsch und t ist der Nullwert vom Typ T, und es tritt keine Panik auf.

HINWEIS: Der Wert isollte vom Schnittstellentyp sein .

Tücken

Auch wenn ies sich um einen Schnittstellentyp handelt, []ihandelt es sich nicht um einen Schnittstellentyp. Um in []iseinen Werttyp zu konvertieren , müssen wir dies daher einzeln tun:

// var items []i
for _, item := range items {
    value, ok := item.(T)
    dosomethingWith(value)
}

Performance

Die Leistung kann langsamer sein als der direkte Zugriff auf den tatsächlichen Wert, wie in dieser Stackoverflow-Antwort gezeigt .

Cizixs
quelle
13
//an easy way:
str := fmt.Sprint(data)
Yuanbo
quelle
21
Fügen Sie eine Erklärung mit Antwort hinzu, wie diese Antwort OP bei der Behebung des aktuellen Problems
hilft
2

Auf Anfrage von @ ρяσsρєя finden Sie eine Erklärung unter https://golang.org/pkg/fmt/#Sprint . Zugehörige Erklärungen finden Sie unter https://stackoverflow.com/a/44027953/12817546 und unter https://stackoverflow.com/a/42302709/12817546 . Hier ist die vollständige Antwort von @ Yuanbo.

package main

import "fmt"

func main() {
    var data interface{} = 2
    str := fmt.Sprint(data)
    fmt.Println(str)
}
Tom J.
quelle
1
Ich denke, Sie könnten beide Antworten kombinieren, indem Sie einfach @ Yuanbo's bearbeiten - Sie werden beide gutgeschrieben und addieren Ihre jeweilige Nützlichkeitsbewertung 😉
Gwyneth Llewelyn