2012-07-09 11 views
60

¿Cuál es la forma idiomática de emitir valores de retorno múltiples en Go?Forma idiomática de hacer una aserción de conversión/tipo en valores de retorno múltiples en Ir

¿Puede hacerlo en una sola línea, o necesita usar variables temporales como las que he hecho en mi ejemplo a continuación?

package main 

import "fmt" 

func oneRet() interface{} { 
    return "Hello" 
} 

func twoRet() (interface{}, error) { 
    return "Hejsan", nil 
} 

func main() { 
    // With one return value, you can simply do this 
    str1 := oneRet().(string) 
    fmt.Println("String 1: " + str1) 

    // It is not as easy with two return values 
    //str2, err := twoRet().(string) // Not possible 
    // Do I really have to use a temp variable instead? 
    temp, err := twoRet() 
    str2 := temp.(string) 
    fmt.Println("String 2: " + str2) 


    if err != nil { 
     panic("unreachable") 
    } 
} 

Por cierto, se llama casting cuando se trata de interfaces?

i := interface.(int) 

Respuesta

55

No puede hacerlo en una sola línea. Su enfoque de variable temporal es el camino a seguir.

Por cierto, ¿se llama fundición cuando se trata de interfaces?

En realidad se llama type assertion. Un tipo fundido conversión es diferente:

var a int 
var b int64 

a = 5 
b = int64(a) 
+10

En realidad, eso tampoco se llama molde. Se llama una conversión. http://golang.org/ref/spec#Conversions –

+1

Gracias por responder a ambas preguntas. He editado el título para que sea más adecuado a la pregunta. – ANisus

11

template.Must es el enfoque de la biblioteca estándar para devolver sólo el primer valor de retorno en un comunicado. Se podría hacer lo mismo para su caso:

func must(v interface{}, err error) interface{} { 
    if err != nil { 
     panic(err) 
    } 
    return v 
} 

// Usage: 
str2 := must(twoRet()).(string) 

Mediante el uso de must que básicamente dice que nunca debería haber un error, y si la hay, entonces el programa no puede (o al menos no debería) mantener operativo , y entrará en pánico.

+4

Es unidiomático entrar en pánico por errores que no fueron causados ​​por un programador. Una función como 'template.Must()' se usa normalmente para cargar plantillas de una variable en declaraciones de nivel de paquete y funciones 'init()'. La idea es entrar en pánico inmediatamente en el tiempo de ejecución y dejar en claro que algo que el compilador no pudo detectar es incorrecto. No deberías usar esto para evitar variables temporales. –

+1

¿Y qué parte de "Al usar' debe 'usted básicamente dice que nunca debería haber un error" no estaba claro? No debe usar esto a menos que el error solo pueda ser causado por un programador o que el programa realmente no pueda continuar si hay un error. –

+1

No estoy de acuerdo con "o el programa realmente no puede continuar si hay un error". En ese caso, todavía no debes entrar en pánico. –

24
func silly() (interface{}, error) { 
    return "silly", nil 
} 

v, err := silly() 
if err != nil { 
    // handle error 
} 

s, ok := v.(string) 
if !ok { 
    // the assertion failed. 
} 

pero es más probable lo que realmente quiere es utilizar un interruptor de tipo, al igual-a-esto:

switch t := v.(type) { 
case string: 
    // t is a string 
case int : 
    // t is an int 
default: 
    // t is some other type that we didn't name. 
} 

Go es realmente más sobre la corrección de lo que se trata de concisión.

+0

Aún así, creo que Go tiene éxito en ser bastante terminante sin renunciar a la corrección. Para otros que ven esta pregunta, creo que es bueno que hayas mencionado la verificación de afirmación. En mi caso, aunque ya sé qué tipo está almacenado en la interfaz, me salté el cheque. Incluso pensé en usar un 'Puntero inseguro' en lugar de' interfaz {} ', pero supuse que no había suficiente sobrecarga en la afirmación de tipo que justificaba el uso de punteros inseguros. – ANisus

+1

Probablemente debería señalar que la forma idiomática de hacer esto es no devolver la interfaz {}, sino aceptar la interfaz {} como parámetro de entrada, verificar el tipo que se pasó y luego completar ese tipo. Esto se ejemplifica muy bien en el método Unmarshal del paquete de codificación/json. Estas técnicas no cubren todas las mismas situaciones, por lo que no siempre es posible, pero en general es mejor aceptar la interfaz {} que devolver la interfaz {}. Puede usar el paquete de reflejos para verificar que el valor proporcionado sea un puntero. – jorelli

2

O simplemente en un solo caso:

if v, ok := value.(migrater); ok { 
    v.migrate() 
} 

Go se hará cargo de los actores dentro de la cláusula y si le permiten acceder a las propiedades del tipo de fundido.

Cuestiones relacionadas