2015-01-13 10 views
7

Zgodnie z odpowiedzią fortyforty do this question:nieskończona pętla wytwarzać fmt.Sprint (e) wewnątrz metodą błędów

fmt.Sprint(e) wywoła e.Error() konwertować wartość e do string. Jeśli metoda Error() wywoła , wówczas program powtarza się, aż do wyczerpania pamięci.

można przerwać rekurencję przekształcając e do wartości bez metody String lub Error.

To wciąż jest mylące dla mnie. Dlaczego fmt.Sprint (e) wywołuje funkcję e.Error() zamiast String()? Próbowałem przy użyciu interfejsu Stringer, to mój kod:

package main 

import (
    "fmt" 
    "math" 
) 

type NegativeSqrt float64 

func (e NegativeSqrt) Error() string { 
    fmt.Printf(".") 
    return fmt.Sprint(e) 
} 

func (e NegativeSqrt) String() string { 
    return fmt.Sprintf("%f", e) 
} 

func Sqrt(x float64) (float64, error) { 
    if x < 0 { 
    return 0, NegativeSqrt(x) 
    } 
    return math.Sqrt(x), nil 
} 

func main() { 
    fmt.Println(Sqrt(2)) 
    fmt.Println(Sqrt(-2)) 
} 

Odpowiedz

9

Wydaje się to wyjaśnić directly jest źródłem pakietu fmt:

// Is it an error or Stringer? 
// The duplication in the bodies is necessary: 
// setting handled and deferring catchPanic 
// must happen before calling the method. 

I niż Error() lub String() nazywa.

Co oznacza, że ​​pierwszy error.Error() jest wywoływany w celu utworzenia łańcucha, który jest następnie przetwarzany ponownie i drukowany jako ciąg.

To, czy error ma metodę String, jest tutaj nieistotne. Pytanie brzmi, dlaczego NegativeSqrt jest drukowany jedną metodą, a nie drugą. Rodzaj NegativeSqrt realizuje zarówno fmt.Stringer i error interfejsy, więc jest to do wykonania fmt pakietu, który z interfejsów należy stosować, aby uzyskać string z NegativeSqrt (od fmt.Sprint wykonuje swoje parametry przez interface{}).

Aby zilustrować ten Rozważmy następujący przykład:

package main 

import (
    "fmt" 
) 

type NegativeSqrt float64 

func (e NegativeSqrt) Error() string { 
    return "" 
} 

func (e NegativeSqrt) String() string { 
    return "" 
} 

func check(val interface{}) { 
    switch val.(type) { 
    case fmt.Stringer: 
     fmt.Println("It's stringer") 
    case error: 
     fmt.Println("It's error") 
    } 
} 

func check2(val interface{}) { 
    switch val.(type) { 
    case error: 
     fmt.Println("It's error") 
    case fmt.Stringer: 
     fmt.Println("It's stringer") 
    } 
} 

func main() { 
    var v NegativeSqrt 
    check(v) 
    check2(v) 
} 

Wykonanie to daje:

% go run a.go 
It's stringer 
It's error 

To dlatego Przejdź przełącznik typu zachowuje się jak normalny przełącznik, więc order of cases matters.

+0

Po "edycji" ta odpowiedź wyjaśnia teraz, dlaczego Takie zachowanie, dzięki. –

+0

_ ** Edytuj: ** Poprawiłem link. Podany link nie działa. podawaj linki, wskazując na commit, a nie na mistrza, może się zmienić. –

1

Ponieważ typ jest error i interfejs error jest

type error interface{ 
    Error() string 
    } 

każdy error musi mieć metodę Error() string ale nie musi mieć metodę String() string. To dlatego logika polega na sprawdzeniu najpierw metody Error().

+0

Odpowiedź Tomasza Kłaka również jest genialna, ale to wyjaśnia, dlaczego jest ona realizowana w ten sposób. –

+0

Nie sądzę, że to pytanie jest na temat/poprawne. – tumdum

0

Pozwolę sobie rozszerzyć znalezienie tumdum dla lepszej przejrzystości.

Odskoczę od połączenia, aby sprawdzić, jak wchodzimy w pętlę.

Zaczynamy od ćwiczeń na

func (e NegativeSqrt) Error() string { 
    fmt.Printf(".") 
    return fmt.Sprint(e) 
} 

który dostarcza nam linię 237 fmt/print.go:

func Sprint(a ...interface{}) string 

Wewnątrz funkcji, nasz następny skok jest na linii 239:

p.doPrint(a, false, false) 

Dojeżdżamy do linii 1261:

func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { 

Wewnątrz tej funkcji, będziemy skakać z naszej error argumentu przez linię 1273:

prevString = p.printArg(arg, 'v', 0) 

Dojeżdżamy ogromnym funkcji rdzenia potwora na linii 738:

func (p *pp) printArg(arg interface{}, verb rune, depth int) (wasString bool) { 

Wewnątrz możesz zobaczyć duży przełącznik switch case. error przechodzi do sekcji default, ponieważ jest uważany za nietrywialny.

który dostarcza nam linia 806 z wezwaniem do handleMethods():

if handled := p.handleMethods(verb, depth); handled { 

Dojeżdżamy linii 688:

func (p *pp) handleMethods(verb rune, depth int) (handled bool) { 

Wewnątrz tej funkcji, na linii 724, zadzwoń do Error() dzieje, który kończy pętlę:

p.printArg(v.Error(), verb, depth) 
Powiązane problemy