невозможно преобразовать данные (интерфейс типа {}) в строку типа: необходимо утверждение типа

178

Я довольно новичок, и я играл с этим пакетом уведомлений .

Сначала у меня был код, который выглядел так:

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

Я хотел добавить новую строку, Hello World!но не в функции doitвыше, потому что это было бы довольно тривиально, но в дальнейшем, handlerкак это показано ниже:

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

После go run:

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

После небольшого поиска в Google я нашел этот вопрос на SO .

Затем я обновил свой код:

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

Это то, что я должен был сделать? Ошибки моего компилятора исчезли, так что я думаю, это довольно хорошо? Это эффективно? Стоит ли делать это по-другому?

Альфред
источник

Ответы:

292

Согласно спецификации Go :

Для выражения x типа интерфейса и типа T основное выражение x. (T) утверждает, что x не равно nil и что значение, хранящееся в x, имеет тип T.

«Утверждение типа» позволяет вам объявить, что значение интерфейса содержит определенный конкретный тип или что его конкретный тип удовлетворяет другому интерфейсу.

В вашем примере вы утверждали, что данные (тип interface {}) имеют конкретную строку типа. Если вы ошибаетесь, программа запаникует во время выполнения. Вам не нужно беспокоиться об эффективности, проверка требует только сравнения двух значений указателя.

Если вы не уверены, была ли это строка или нет, вы можете проверить, используя синтаксис двух возвращаемых значений.

str, ok := data.(string)

Если данные не являются строкой, ok будет false. Тогда принято заключать такое утверждение в утверждение if следующим образом:

if str, ok := data.(string); ok {
    /* act on str */
} else {
    /* not string */
}
Стивен Вайнберг
источник
29

Тип Утверждение

Это известно как type assertion на Голанге, и это обычная практика.

Вот объяснение от тура го :

Утверждение типа обеспечивает доступ к конкретному значению интерфейса.

t := i.(T)

Этот оператор утверждает, что значение интерфейса i содержит конкретный тип T, и присваивает базовое значение T переменной t.

Если я не держу T, оператор вызовет панику.

Чтобы проверить, содержит ли значение интерфейса определенный тип, утверждение типа может возвращать два значения: базовое значение и логическое значение, которое сообщает, успешно ли подтверждено.

t, ok := i.(T)

Если у меня есть T, то t будет базовым значением, и ok будет истинным.

Если нет, то ok будет false, а t будет нулевым значением типа T, и паники не будет.

ПРИМЕЧАНИЕ: значение iдолжно быть типом интерфейса .

Ловушки

Даже если iэто тип интерфейса, []iэто не тип интерфейса. В результате, чтобы преобразовать []iего в тип значения, мы должны сделать это индивидуально:

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

Производительность

Что касается производительности, он может быть медленнее, чем прямой доступ к фактическому значению, как показано в этом ответе stackoverflow .

cizixs
источник
13
//an easy way:
str := fmt.Sprint(data)
Yuanbo
источник
21
Добавьте некоторое объяснение с ответом о том, как этот ответ помогает ОП в исправлении текущей проблемы
ρяσѕρєя K
3

По просьбе @ ρяσѕρєя объяснение можно найти по адресу https://golang.org/pkg/fmt/#Sprint . Соответствующие объяснения можно найти по адресу https://stackoverflow.com/a/44027953/12817546 и по адресу https://stackoverflow.com/a/42302709/12817546 . Вот ответ @ Yuanbo полностью.

package main

import "fmt"

func main() {
    var data interface{} = 2
    str := fmt.Sprint(data)
    fmt.Println(str)
}
Том Дж
источник
1
Я думаю, вы могли бы объединить оба ответа, просто отредактировав @ Yuanbo's - вы оба получите кредит и добавите свой соответствующий балл «полезности» 😉
Гвинет Ллевелин