2012-07-09 9 views
60

Jaki jest idiomatyczny sposób przesyłania wielu wartości zwracanych w Go?Idiomatyczny sposób na konwersję/typ asercji dla wielu wartości zwracanych w Go

Czy możesz to zrobić w pojedynczej linii, czy też musisz użyć zmiennych tymczasowych, takich jak w poniższym przykładzie?

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

Nawiasem mówiąc, jest to nazywane casting jeśli chodzi o interfejsy?

i := interface.(int) 

Odpowiedz

55

Nie można tego zrobić w pojedynczej linii. Twoje tymczasowe zmienne podejście jest drogą do zrobienia.

Nawiasem mówiąc, czy to się nazywa casting, jeśli chodzi o interfejsy?

To tak naprawdę nazywa się type assertion. typ obsada konwersji jest inna:

var a int 
var b int64 

a = 5 
b = int64(a) 
+10

W rzeczywistości nie jest to również rzutowanie. Nazywa się to konwersją. http://golang.org/ref/spec#Conversions –

+1

Dzięki za odpowiedź na oba pytania. Zmieniłem tytuł, aby był bardziej adekwatny do pytania. – ANisus

11

template.Must jest podejście biblioteki standardowej dla powrocie tylko pierwszą wartość zwracaną w jednym sprawozdaniu. Można zrobić podobnie do przypadku:

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

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

Korzystając must można w zasadzie powiedzieć, że nigdy nie powinno być błędu, a jeśli nie jest, to program może nie (a przynajmniej nie powinien) prowadzić eksploatację i zamiast tego wpadnie w panikę.

+4

Panuje na panikę nad błędami, które nie zostały spowodowane przez programistę. Funkcja taka jak 'template.Must()' jest zwykle używana do ładowania szablonów ze zmiennej w deklaracjach poziomu pakietu i funkcji 'init()'. Chodzi o to, aby natychmiast uruchomić panikę w środowisku uruchomieniowym i pokazać, że coś, czego kompilator nie mógł złapać, jest złe. Nie należy tego używać w celu uniknięcia zmiennych tymczasowych. –

+1

A która część "Używając" musi "w zasadzie mówisz, że nigdy nie powinno być błędu" nie było jasne? Nie powinieneś tego używać, chyba że błąd może być spowodowany tylko przez programistę lub program naprawdę nie może być kontynuowany, jeśli wystąpił błąd. –

+1

Nie zgadzam się z "lub program naprawdę nie może być kontynuowany, jeśli wystąpił błąd". W takim przypadku nadal nie należy panikować. –

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

ale bardziej prawdopodobne, co rzeczywiście chcesz jest użycie przełącznika typu, jak to-a-:

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 jest naprawdę więcej o poprawności niż o lapidarnością, jakiej.

+0

Wciąż myślę, że Go również jest dość zwięzły, nie rezygnując z poprawności. Dla innych, którzy widzą to pytanie, myślę, że dobrze, że wspomniałeś o sprawdzaniu twierdzeń. W moim przypadku jednak wiem już, jaki typ jest przechowywany w interfejsie, więc pominąłem czek. Myślałem nawet o używaniu 'unsafe.Pointer' zamiast' interface {} ', ale domyśliłem się, że nie ma wystarczającego obciążenia w twierdzeniu typu uzasadniającym użycie niebezpiecznych wskaźników. – ANisus

+1

Prawdopodobnie powinienem zwrócić uwagę, że idiomatycznym sposobem na to jest niezwrócenie interfejsu {}, ale zaakceptowanie interfejsu {} jako parametru wejściowego, sprawdzenie typu, który został przekazany, a następnie wypełnienie tego typu. Jest to całkiem ładnie przedstawione w metodzie Unmarshal w pakiecie kodowania/json. Techniki te nie obejmują wszystkich tych samych sytuacji, więc nie zawsze jest to możliwe, ale ogólnie lepiej jest zaakceptować interfejs {} niż zwracać interfejs {}. Możesz użyć pakietu reflect, aby sprawdzić, czy podana wartość jest wskaźnikiem. – jorelli

2

lub tylko w jednym przypadku gdy:

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

Go zadba o obsadzie wewnątrz jeśli klauzulę oraz umożliwiają dostęp do właściwości lanego typu.

Powiązane problemy