2015-01-13 2 views
7

Nach fortyforty Antwort auf this question:Eine Endlosschleife durch fmt.Sprint produziert (e) innerhalb der Error-Methode

fmt.Sprint(e) wird e.Error() rufen Sie den Wert e zu einem string zu konvertieren. Wenn die Methode Error()fmt.Sprint(e) aufruft, wird das Programm solange rekursiv ausgeführt, bis kein Speicher mehr verfügbar ist.

Sie können die Rekursion durchbrechen, indem die e auf einen Wert ohne ein Verfahren String oder Error konvertieren.

Das ist immer noch verwirrend für mich. Warum ruft fmt.Sprint (e) e.Error() statt String() auf? Ich versuchte, die Stringer-Schnittstelle, das ist mein Code:

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

Antwort

9

Es scheint, es erklärt directly ist Quelle von fmt Paket:

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

Und als Error() oder String() aufgerufen.

Was bedeutet es, dass zuerst error.Error() aufgerufen wird, um eine Zeichenfolge zu erzeugen, die als einmal wieder verarbeitet und als Zeichenfolge gedruckt wird.

Ob error hat Methode String ist hier irrelevant. Die Frage ist, warum NegativeSqrt mit einer Methode gedruckt wird und nicht die andere. Typ NegativeSqrt implementiert sowohl fmt.Stringer und error Schnittstellen, so dass es bis zur Umsetzung von fmt Paket ist, die von Schnittstellen verwendet werden soll string von NegativeSqrt zu bekommen (da fmt.Sprint seine Parameter durch interface{} nimmt).

Um dies zu verdeutlichen dieses Beispiel betrachten:

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

Ausführen ergibt dies:

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

Dies liegt daran, in Go Schalter wie normale Schalter verhält, so order of cases matters.

+0

Nach Ihrer "edit", diese Antwort jetzt erklärt, warum wir ha Dieses Verhalten, danke. –

+0

_ ** Bearbeiten: ** Ich habe den Link repariert. Der angegebene Link funktionierte nicht. Geben Sie Links an, indem Sie auf einen Commit zeigen, nicht auf den Master, er kann sich ändern._ –

1

Da der Typ error und die Schnittstelle von error ist

type error interface{ 
    Error() string 
    } 

jeder error muss eine Error() string Methode haben, aber muss nicht ein String() string Methode haben. Deshalb ist die Logik, zuerst die Error() Methode zu überprüfen.

+0

Tomasz Kłaks Antwort ist ebenfalls brillant, aber dieser erklärt, warum er so implementiert wird. –

+0

Ich glaube nicht, dass diese Frage zum Thema/richtig ist. – tumdum

0

Lassen Sie mich Tumdums Befund für bessere Klarheit erweitern.

Ich werde von einem Anruf zu Anruf springen, um zu zeigen, wie wir in die Schleife gehen.

Wir gehen von der Ausübung der

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

Was uns zu einer Linie 237 der fmt/print.go liefert:

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

Innerhalb der Funktion, unser nächster Sprung auf Zeile 239:

p.doPrint(a, false, false) 

Wir kommen zu Zeile 1261:

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

Innerhalb dieser Funktion springen wir mit unseren error Argument, durch die Leitung 1273:

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

Wir sind an einem großen Kern Monster Funktion in Zeile 738 ankommen:

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

Innen Sie können einen großen Schalter sehen. error geht in den default Abschnitt, da es als ein nicht-trivialer Typ gilt.

Was uns 806 mit dem Aufruf an handleMethods() der Leitung liefert:

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

Wir an der Linie ankommen 688:

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

Innerhalb dieser Funktion auf der Leitung 724, rufen zu Error() geschieht, was macht die Schleife:

p.printArg(v.Error(), verb, depth) 
Verwandte Themen