nie można konwertować danych (typ interface {}) do typu łańcuchowego: potrzeba potwierdzenia typu

178

Jestem całkiem nowy i bawiłem się tym pakietem powiadomień .

Na początku miałem kod, który wyglądał tak:

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

Chciałem dodać znak nowej linii do powyższej Hello World!funkcji doit, ale nie w powyższej funkcji , ponieważ byłoby to dość trywialne, ale handlerpóźniej tak jak poniżej:

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

Po go run:

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

Po trochę googlowania znalazłem to pytanie na SO .

Następnie zaktualizowałem mój kod do:

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

Czy to właśnie miałem zrobić? Moje błędy kompilatora zniknęły, więc myślę, że to całkiem nieźle? Czy to jest wydajne? Czy powinieneś zrobić to inaczej?

Alfred
źródło

Odpowiedzi:

292

Zgodnie ze specyfikacją Go :

Dla wyrażenia x typu interfejsu i typu T, podstawowe wyrażenie x. (T) stwierdza, że ​​x nie jest zerowe i że wartość przechowywana w x jest typu T.

„Asercja typu” pozwala zadeklarować, że wartość interfejsu zawiera określony konkretny typ lub że jego konkretny typ spełnia wymagania innego interfejsu.

W twoim przykładzie, twierdziłeś, że dane (typ interfejs {}) mają konkretny ciąg typu. Jeśli się pomylisz, program będzie panikować w czasie wykonywania. Nie musisz martwić się o wydajność, sprawdzenie wymaga jedynie porównania dwóch wartości wskaźnika.

Jeśli nie masz pewności, czy to łańcuch, czy nie, możesz przetestować, używając składni dwóch zwrotów.

str, ok := data.(string)

Jeśli dane nie są ciągiem znaków, ok będzie fałszywe. Powszechne jest wtedy zawijanie takiego oświadczenia do instrukcji if, jak to:

if str, ok := data.(string); ok {
    /* act on str */
} else {
    /* not string */
}
Stephen Weinberg
źródło
29

Asercja typu

Jest to znane jako type assertiongolang i jest to powszechna praktyka.

Oto wyjaśnienie z wycieczki w go :

Asercja typu zapewnia dostęp do konkretnej wartości bazowej wartości interfejsu.

t := i.(T)

Ta instrukcja potwierdza, że wartość interfejsu i zawiera konkretny typ T i przypisuje podstawową wartość T do zmiennej t.

Jeśli nie trzymam T, stwierdzenie wywoła panikę.

Aby sprawdzić, czy wartość interfejsu zawiera określony typ, potwierdzenie typu może zwrócić dwie wartości: wartość bazową i wartość logiczną, która informuje, czy asercja się powiodła.

t, ok := i.(T)

Jeśli mam T, to t będzie wartością bazową i ok będzie prawdziwe.

Jeśli nie, ok będzie fałszywe, at będzie zerową wartością typu T i nie będzie paniki.

UWAGA: wartość ipowinna być typem interfejsu .

Pułapki

Nawet jeśli ijest typem interfejsu, []inie jest typem interfejsu. W efekcie, aby przeliczyć się []ina jego typ wartości musimy zrobić to indywidualnie:

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

Występ

Jeśli chodzi o wydajność, może być wolniejszy niż bezpośredni dostęp do rzeczywistej wartości, jak pokazano w tej odpowiedzi na temat przepełnienia stosu .

cizixs
źródło
13
//an easy way:
str := fmt.Sprint(data)
Yuanbo
źródło
21
Dodaj wyjaśnienie wraz z odpowiedzią, w jaki sposób ta odpowiedź pomaga OP w naprawianiu bieżącego problemu
ρяσѕρєя K
3

Zgodnie z prośbą @ ρяσѕρєя wyjaśnienie można znaleźć na stronie https://golang.org/pkg/fmt/#Sprint . Powiązane wyjaśnienia można znaleźć pod adresem https://stackoverflow.com/a/44027953/12817546 i https://stackoverflow.com/a/42302709/12817546 . Oto pełna odpowiedź @ Yuanbo.

package main

import "fmt"

func main() {
    var data interface{} = 2
    str := fmt.Sprint(data)
    fmt.Println(str)
}
Tom J.
źródło
1
Wydaje mi się, że możesz połączyć obie odpowiedzi, po prostu edytując @ Yuanbo - oboje zostaniecie uznani i zsumujecie swój odpowiedni wynik „użyteczności” 😉
Gwyneth Llewelyn