no se pueden convertir datos (escriba la interfaz {}) para escribir una cadena: necesita una aserción de tipo

178

Soy bastante nuevo y estaba jugando con este paquete de notificación .

Al principio tenía un código que se veía así:

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

Quería agregar nueva línea a, Hello World!pero no en la función doitanterior, porque eso sería bastante trivial, pero a handlercontinuación, como esto a continuación:

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

Después go run:

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

Después de buscar un poco en Google, encontré esta pregunta en SO .

Luego actualicé mi código a:

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

¿Es esto lo que se suponía que debía hacer? Mis errores de compilación han desaparecido, así que supongo que eso es bastante bueno. ¿Es esto eficiente? ¿Deberías hacerlo de manera diferente?

Alfredo
fuente

Respuestas:

292

De acuerdo con la especificación Go :

Para una expresión x de tipo de interfaz y un tipo T, la expresión primaria x. (T) afirma que x no es nulo y que el valor almacenado en x es de tipo T.

Una "aserción de tipo" le permite declarar que un valor de interfaz contiene cierto tipo concreto o que su tipo concreto satisface otra interfaz.

En su ejemplo, estaba afirmando que los datos (interfaz de tipo {}) tienen la cadena de tipo concreta. Si está equivocado, el programa entrará en pánico en tiempo de ejecución. No necesita preocuparse por la eficiencia, la verificación solo requiere comparar dos valores de puntero.

Si no estaba seguro de si era una cadena o no, podría probar usando la sintaxis de retorno de dos.

str, ok := data.(string)

Si los datos no son una cadena, ok será falso. Entonces es común envolver dicha declaración en una declaración if así:

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

Afirmación de tipo

Esto se conoce como type assertionen golang, y es una práctica común.

Aquí está la explicación de un recorrido por go :

Una aserción de tipo proporciona acceso al valor concreto subyacente de un valor de interfaz.

t := i.(T)

Esta declaración afirma que el valor de la interfaz i contiene el tipo concreto T y asigna el valor T subyacente a la variable t.

Si no tengo una T, la declaración provocará un pánico.

Para probar si un valor de interfaz contiene un tipo específico, una aserción de tipo puede devolver dos valores: el valor subyacente y un valor booleano que informa si la aserción tuvo éxito.

t, ok := i.(T)

Si tengo una T, entonces t será el valor subyacente y ok será verdadero.

De lo contrario, ok será falso y t será el valor cero del tipo T, y no se producirá pánico.

NOTA: el valor idebe ser el tipo de interfaz .

Trampas

Incluso si ies un tipo de interfaz, []ino es un tipo de interfaz. Como resultado, para convertir []ia su tipo de valor, tenemos que hacerlo individualmente:

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

Actuación

En cuanto al rendimiento, puede ser más lento que el acceso directo al valor real como se muestra en esta respuesta de stackoverflow .

cizixs
fuente
13
//an easy way:
str := fmt.Sprint(data)
Yuanbo
fuente
21
Agregue alguna explicación con la respuesta sobre cómo esta respuesta ayuda a OP a solucionar el problema actual
ρяσѕρєя K
3

Según lo solicitado por @ ρяσѕρєя, se puede encontrar una explicación en https://golang.org/pkg/fmt/#Sprint . Se pueden encontrar explicaciones relacionadas en https://stackoverflow.com/a/44027953/12817546 y en https://stackoverflow.com/a/42302709/12817546 . Aquí está la respuesta completa de @Yuanbo.

package main

import "fmt"

func main() {
    var data interface{} = 2
    str := fmt.Sprint(data)
    fmt.Println(str)
}
Tom J
fuente
1
Supongo que podrían combinar ambas respuestas simplemente editando @ Yuanbo: ambos serán acreditados y sumarán su puntaje de 'utilidad' respectivo 😉
Gwyneth Llewelyn