Go: Vorschlag: "if err != nil" in Ruhe lassen?

Erstellt am 28. Juni 2019  ·  314Kommentare  ·  Quelle: golang/go

Der Go2-Vorschlag #32437 fügt der Sprache eine neue Syntax hinzu, um das if err != nil { return ... } Boilerplate weniger schwerfällig zu machen.

Es gibt verschiedene alternative Vorschläge: #32804 und #32811, da das Original nicht überall beliebt ist.

Um eine weitere Alternative in den Mix zu werfen: Warum nicht so belassen

Mir gefällt die explizite Natur des if err != nil Konstrukts und als solche verstehe ich nicht, warum wir dafür eine neue Syntax brauchen. Ist es wirklich so schlimm?

FrozenDueToAge Proposal Proposal-Hold error-handling

Hilfreichster Kommentar

Es sollte nur einen Weg geben, etwas zu tun

Alle 314 Kommentare

Ich unterstütze das. Ich mag es wirklich, wie jeder Fehler vor der Rückgabe eine menschenlesbare Dokumentation zur Quelle hinzufügt (normalerweise formatieren wir unsere Fehler als "konnte nicht [was ich in diesen Codezeilen tue]: [vorheriger Fehler]") und auch den Benutzern Lesefehler.

Auf diese Weise generierte Fehler sind äußerst informativ und viel einfacher zu lesen als Stacktraces. Gedruckte Fehler, die Stack-Traces enthalten, gehen normalerweise davon aus, dass Sie Zugriff auf Quellen haben (Administratoren haben möglicherweise keinen solchen Zugriff) und sich tatsächlich im Code auskennen.

Fehler ohne Kontext oder Ablaufverfolgung (der bloße String "EOF") sind absolut nutzlos. Ich denke, dass die Verwendung von Verknüpfungen, die es einfacher machen, nackte Fehler zurückzugeben, dazu führt, dass Go-Programme viele nutzlose Fehler ausgeben.

Wenn überhaupt, sollten wir das Dekorieren von Fehlern mit Kontext vorantreiben und unterstützen, vielleicht mit neuen Tierarzt- und Fusselregeln.

Ich mag auch die explizite Fehlerprüfung. try ist verwirrend und die implizite Rückgabe seltsam.

Ich denke, anstatt Fehler zu überdenken, könnten wir einen alternativen Ansatz ausprobieren, um diese Überprüfungen zu verkürzen.

Hier ein Beispiel, dem ich nicht unbedingt zustimme:

value, err := foo()
return err if err != nil

Dies würde einen kürzeren, aber dennoch expliziten Ansatz ermöglichen. Und es würde das Hinzufügen von Kontext ermöglichen!

Das heißt, Inline-IFs sind eine Ruby-Sache und fühlen sich nicht sehr Goish an, aber das ist nur Brainstorming. Vielleicht finden wir noch etwas.


EDIT: Ich habe hier einen Vorschlag dazu hinzugefügt: https://github.com/golang/go/issues/32860

Es sollte nur einen Weg geben, etwas zu tun

[...] Warum nicht so belassen wie es ist?

Ich denke, es ist fair zu sagen, dass wir alle die Antwort darauf kennen. Sie brauchen nur einen der verschiedenen Vorschläge zu lesen, um die Antwort herauszufinden, wenn Sie es aufrichtig nicht wissen.

IMO, es gibt hier zu wenig Details, um eine fokussierte Diskussion zu führen (dh ich denke nicht, dass es als Vorschlag gilt) und es wird bald zu einem weiteren Fahrradschuppen voller Kreise und Ideen, die den Code weniger lesbar machen .

So viel dazu.

Ich bin wohl wegen dieser expliziten Fehlerbehandlung zu Go gekommen. Es liegt irgendwo zwischen implizitem Try-Catch, das viele Sprachen bevorzugen, und Funktionstypen wie Option oder Maybe, die es bevorzugen, an den Benutzer zurückgegeben und explizit behandelt zu werden.

Ich bin mir nicht sicher, ob ein neues Konstrukt das wirklich lösen würde. Wenn Sie if err := nil in eine Hilfsfunktion wie diese gepackt haben, könnte dies ein wenig helfen (verzeihen Sie mein eingerostetes Go):

func handleErr(err error, cb func(error)) {
        if err := nil {
                cb(err)
        }
}

Das Problem, das diese Hilfsfunktion jedoch weniger nützlich macht, ist das Typsystem, das ein anderes Thema ist.

Ich unterstütze das. if err != nil { return err } ist nicht Teil eines Codes in unserer Codebasis. Daher macht der Versuch "Makro" überhaupt keinen Sinn. Wir geben nur umschlossene Fehler mit einer Nachricht zurück, die den Kontext beschreibt.

Das Hinzufügen von Kontext per Defer ist auch nicht sinnvoll, da wir verschiedene Fehlermeldungen zurückgeben möchten, um die verschiedenen Fehlerarten zu unterscheiden. Ein try(fn(), "my error message: %w") könnte jedoch nützlich sein. Aber selbst dann könnte das if err != nil Konstrukt aufgrund der kürzeren Zeilenlängen immer noch vorzuziehen sein.

Ehrlich gesagt möchte ich keine implizite Rückgabe, die try liefert. Wenn wir Generika hätten, würde ich eine Lösung bevorzugen, die stattdessen monadisches Verhalten verwendet.

type Result<T> interface {
  Expect(err error) T
  OrElse(defaultValue T) T
}

func From<T>(value T, err error) Result<T> { ... }

Für mich ist dies viel sauberer als das derzeit vorgeschlagene integrierte, obwohl weitere Änderungen an den oben genannten erforderlich wären, da Sie eine Vielzahl von Methoden haben, die (Wert, Fehler) und Ergebnis zurückgeben

Der aktuelle Vorschlag von try , der keine Möglichkeit hat, die Fehler explizit zu dekorieren, entspricht nicht meinen Anforderungen. Ich kann mir nicht vorstellen, es jemals zu benutzen. Ehrlich gesagt könnte es genauso gut code_smell heißen.

Es macht möglicherweise keinen Sinn, es zu ändern, weil das falsche Problem gelöst werden soll.

Der uns bekannte Code ist keine Fehlerbehandlung.

if err != nil {
  return err
}

Dies ist die Fehlerbehandlung nil . An keiner Stelle in diesem Muster wird der Wert eines Fehlers behandelt.

Wenn ich das in einer anderen Sprache demonstrieren würde, Ruby.

begin
 some_method_that_raises_an_error
rescue => e # catch any exception
  retry e        # throw it up again
end

Dies überträgt das gleiche Verhalten wie der Golang-Code. Wenn wir feststellen, dass eine Ausnahme aufgetreten ist, und diese dann erneut auslösen. Wir werfen es einfach auf den Stapel.

In Golang return es.

Wo findet die eigentliche _Fehlerbehandlung_ statt?

Wir alle haben ähnliche Erfahrungen mit dem Scheitern dieses Musters gemacht. Wenn Sie beispielsweise einen file not found Fehler erhalten und dann viel Zeit damit verbringen, den ursprünglichen _thrower_ dieses Fehlers zu verfolgen.

Aus diesem Grund glaube ich, dass der Vorschlag von try (und andere) fehlerhaft ist. Wir haben kein gutes Muster für den tatsächlichen Umgang mit Fehlern.

Ich habe err.Error() String-Überprüfung, Typ-Assertionen usw. gesehen, um den Fehler tatsächlich zu untersuchen.
Wir brauchen ein Muster für diese Inkonsistenz. Es fühlt sich an, als ob xerrs dies lösen könnte, aber es fühlt sich auch noch nicht vollständig an.

Ich unterstütze, dass err!=nil unverändert bleibt.

Jedes Mal, wenn ich mich mit einer beträchtlichen Go-Codebasis auseinandersetze, frage ich mich, wie ich einen Teil der Boilerplate reduzieren kann. Ich komme immer wieder auf:

  • Diese Codepfade existieren auf die eine oder andere Weise.
  • Selbst wenn Sie den Codepfad nicht verbergen müssen, wird dies zum Standardverhalten, wenn Sie den Leuten die Möglichkeit geben, ihn auszublenden (denn anscheinend messen wir immer noch, wie schwer eine Sprache zu verwenden ist, durch die Anzahl der Zeilen).
  • Wenn das Standardverhalten Codepfade verbirgt, würde ich nach neuen "fehlenden Bereinigungsfehlern" Ausschau halten.
  • Die Bedeutung und die Muster der zurückgegebenen Fehler sind so vielfältig, dass dieser Vorschlag nur einen Teil des wahrgenommenen Problems erfassen würde
  • Wenn nur ein Teil erfasst wird, würden wir sicherlich eine Reihe von Lösungen erhalten
  • Bei einer Reihe von Lösungen wäre die Versuchung groß, eine adaptive Magie für Anwendungsfälle zu verwenden, um sie zusammenzufassen
  • Wenn dies tatsächlich ein Problem wäre, dann steht es den Menschen frei, ihre eigene einfache Lösung zu entwickeln oder ein massenhaft übernommenes Muster zu verwenden. Ich habe so etwas noch nicht gesehen. Vielleicht habe ich einfach nicht genau genug gesucht.

Der Issue Tracker ist für viele Dinge nützlich, aber für eine detaillierte Diskussion eines komplexen Themas ist er nicht nützlich. Der Issue Tracker bietet kein Threading und das Antworten auf eine bestimmte Nachricht ist umständlich. Da es hier keinen wirklichen Vorschlag gibt, sondern nur eine Reaktion auf andere Vorschläge, ermutige ich Sie wirklich dringend, diese Diskussion auf die Mailingliste von golang-nuts zu bringen.

Wenn ich darf, glaube ich, dass dies die Antwort ist. Dieser neue Fehlervorschlag steht in direktem Widerspruch zu den Zielen der Sprache.

Der Grund, warum ich Golang liebe, liegt in seiner Einfachheit und klaren Verwendung des Kontrollflusses. Eines der Dinge, die ich an Java am meisten verachte, ist das try Throw-Konstrukt. Es ist so widerlich. Es fördert schreckliche Fehlerbehandlung. Das Senden von Ausnahmen in der Aufrufliste ist eine schreckliche und ekelhafte Methode zur Handhabung des Kontrollflusses. Darüber hinaus ermutigt es, alles in einen riesigen Scheck zu packen und es zu beenden, anstatt jede Fehlersituation selbst zu dokumentieren und explizit zu behandeln.

If err != nil fördert eine gute Fehlerbehandlung, ist selbstdokumentierend und fördert eine gute Dokumentation in Bezug auf den spezifischen Fall, und es ist ehrlich gesagt eines der Dinge, die ich am meisten an go liebe. Diesen neuen Kontrollfluss unterbrechen zu lassen, mit unordentlichen, etwas mehrdeutigen Rückgaben und Parametern und verwirrender Semantik zu arbeiten, entspricht nicht dem Geist der Sprache, die ich mittlerweile verehre.

Ausführlichkeit ist keine schlechte Sache. Unnötige Ausführlichkeit ist, aber ich würde argumentieren, dass die Fehlerbehandlung von go nicht unnötig ist. Es ist ein Teil des Charmes der Sprache.

Konnte nicht mehr zustimmen. Die explizite Fehlerbehandlung ist eines der besten Features der Sprache IMO. Ich habe immer das Gefühl, dass viele, die sich daran stören, nur noch nicht daran gewöhnt sind.

Es ist nicht gut, wenn die Themen getrennt werden, aber ich denke, dass in diesem Fall zwei Meinungen zu einer Meinung zusammengeführt werden.

  1. Wir mögen keine neue Syntax (versuchen Sie oder neue if-err-Syntax)
  2. Wie auch immer, wir wollen keine neue Syntax hinzufügen

GitHub-Abstimmungssymbole können die zweite nicht interpretieren.

Die explizite Fehlerbehandlung in go ist einer der Gründe, warum ich golang liebe. Ich verstehe nicht, warum es ein Go-Entwickler anders will. Ich denke, der Vorschlag, eine neue Syntax hinzuzufügen, kommt hauptsächlich von Leuten, die mit der Syntax anderer Sprachen vertraut sind. Es kann etwas gewöhnungsbedürftig sein, aber es funktioniert perfekt, wenn Sie sich daran gewöhnt haben.

Ich habe #32811 geschrieben und unterstütze diesen Vorschlag mehr ... Ich würde die Fehlerbehandlung lieber in Ruhe lassen. Ich denke, die Emoji-Reaktionen auf diesen Vorschlag sagen viel aus.

Ich persönlich bin damit einverstanden, die Fehlerbehandlung so zu belassen, wie sie ist. Eines der Dinge, die ich an Go mag, ist, dass die Sprache minimal ist und im Allgemeinen eine Möglichkeit hat, Dinge zu tun. Durch Hinzufügen einer neuen Syntax für die Fehlerbehandlung erstellen wir eine Welt, in der x% des Codes die aktuelle Methode verwendet und y% die neue Methode verwendet. Dies wird, neben anderen bereits besprochenen Problemen, inkonsistente Codebasen schaffen. Ich persönlich glaube nicht, dass der Wert der neuen Fehlerbehandlungssyntax die Kompromisse wert ist, da ich die vorhandene Syntax für ausreichend/ausreichend halte.

Als jemand, der eine neuere Version auf Golang ist, ist eines der Dinge , dass ich erfrischend über die Sprache zu finden , ist die explizite Fehlerbehandlung. Ich habe ziemlich viel mit Java, Ruby, Python und Node gearbeitet, und der Umgang mit Fehlern ist viel mühsamer als in Go. Ich würde lieber den klaren 'Pfad' von Fehlern sehen, als ihn mir durch ein Sprachkonstrukt andeuten zu lassen, das ihn noch unbestimmter macht.

ˋreturn ... if ...ˋ Vorschlag von @andreynering ist eigentlich ziemlich schlau imho. Hält den Code explizit (keine versteckte Kontrollflussunterbrechung), während die Boilerplate reduziert wird (einzeilig).

Stimmen Sie zu, lassen Sie if err != nil Ruhe.

Ich bevorzuge das aktuelle Format. Es ist klar und ein leicht zu vermittelndes Muster. Neue Ingenieure auf den neuesten Stand zu bringen ist einfach, da sie ein einfaches Muster lernen und es wiederholen können. Es fordert die Benutzer auch auf, den Fehler zumindest im aktuellen Kontext zu betrachten, um sicherzustellen, dass zumindest der Ingenieur bestätigt, dass hier ein Fehler auftreten kann und ich darüber nachdenken muss, was zu tun ist.

Ich habe #32804 geschrieben und ich würde viel lieber sehen, dass sich die Dinge NICHT ändern. Wenn Ihr Code lang ist, liegt das daran, dass er eine Menge Dinge tut. Wenn Sie viel Fehlerbehandlungscode haben, liegt das daran, dass Sie alle Ihre Fälle gut bearbeiten.

Bitte, lasst uns nicht Dinge hinzufügen, nur um Dinge hinzuzufügen.

Ich genieße die Einfachheit der Fehlerbehandlung so wie sie ist.

Expect ist nur ein Anagramm, außer, und ich würde es lieber nicht verwenden. Danke, dass du damit angefangen hast.

Bitte ändere meinen heiligen Gral nicht.

Es gab überwältigendes Feedback der Community, das eine optimierte Fehlerbehandlung forderte (aus der jährlichen Umfrage). Das Go-Team befasst sich nun mit diesem Problem.

@icholy Klar, aber die aktuellen Vorschläge lassen zu wünschen übrig. Sie alle scheinen entweder die Fehlerbehandlung zu verschleiern, zu mehr try/catch/finally-Implementierungen zurückzukehren, die Fehlerbehandlung aus dem Kontext zu blasen oder sie auf andere Weise komplizierter zu machen. Da Go eine einfache Sprache sein soll, haben viele von uns auf eine einfache Option gehofft. Ich habe keine gesehen, die mir persönlich gefällt, daher denke ich, dass es die bessere Option ist, das aktuelle Muster beizubehalten.

Eine Beschwerde war, dass man es eingeben musste, aber praktisch jeder Editor hat Verknüpfungen zum Einfügen von Codeschnipseln, also ist es wirklich keine große Sache. Vielleicht ist es meine eigene Erfahrung mit Go seit vor 1.0, aber ich mag die Einfachheit und die Redundanz stört mich nicht.

@kevineaton du denkst try ist kompliziert?

Dem stimme ich voll und ganz zu. Ich bin nicht einmal persönlich davon überzeugt, dass wir etwas tun müssen - ich stimme zu, dass die if err != nil Checks auf den ersten Blick unangenehm aussehen, aber ich habe nichts vorgeschlagen, das das Problem tatsächlich löst, ohne im Großen und Ganzen genau gegen die Dinge zu verstoßen, die gelten beliebt für.

@icholy nachdem ich vor Go zehn Jahre lang Java und Python geschrieben habe, denke ich, dass es sein kann. Ich denke, Sie stoßen auf das Abfangen von Pokemon-Ausnahmen oder das Verketten mehrerer Ausnahmen und führen ansonsten noch mehr Overhead und Boilerplate ein. Ich würde nicht zu dieser Art der Fehlerbehandlung zurückkehren, wenn ich sie jemals vermeiden könnte, da sie fast immer zu Kopfschmerzen und Verwirrung führte, BESONDERS beim Unterrichten. Ich unterrichte auch Informatik zusätzlich zu meiner täglichen Arbeit als Softwarearchitekt, daher bin ich eher auf die Ausbildung neuer Entwickler und das Mentoring ausgerichtet. Ich würde Go wählen und es ist eine einfache Fehlerbehandlung gegenüber einer potenziell komplizierteren oder nuancierteren Fehlerbehandlung.

Der Issue Tracker ist für viele Dinge nützlich, aber für eine detaillierte Diskussion eines komplexen Themas ist er nicht nützlich.

Ist das nicht die Wahrheit. Aber hier sind wir.

if err != nil verschwindet nicht, wenn try hinzugefügt wird. Ich glaube, dass try Codepfaden, die entweder eine schwere Fehlerweiterleitung darstellen oder bei denen viele verschiedene Fehler einfach in einem Defer-Fehlerhandler zusammengefasst werden können, etwas Klarheit verleiht. . Ich verstehe nicht wirklich, wie try ermutigt, Fehler nicht so viel mehr zu behandeln als ein Haufen leerer if-err-return-err . Es ist leicht zu ignorieren, dass die Fehler tatsächlich behandelt werden, unabhängig davon, ob try ist oder nicht. Ich denke, dass try einer der besten Vorschläge für die Fehlerbehandlung ist, da es so aussieht, als ob es einfach sein wird, Code zu lesen, der ihn verwendet.

Meine ungebetenen zwei Cent, es fühlt sich einfach nicht sehr "Go" an. Es ist zu magisch und wir setzen auf implizite Konstrukte statt auf explizite.

Aus den Go- FAQ

Warum hat Go den Operator ?: nicht?
_In Go gibt es keinen ternären Testbetrieb. Sie können Folgendes verwenden, um das gleiche Ergebnis zu erzielen:_

if expr {
   n = trueVal
} else {
    n = falseVal
}

Der Grund dafür, dass ?: in Go fehlt, ist, dass die Entwickler der Sprache gesehen haben, dass die Operation zu oft verwendet wurde, um undurchdringlich komplexe Ausdrücke zu erstellen. Die if-else-Form ist zwar länger, aber fraglos klarer. Eine Sprache benötigt nur ein bedingtes Kontrollflusskonstrukt .

@ianlancetaylor

Der Issue Tracker ist für viele Dinge nützlich, aber für eine detaillierte Diskussion eines komplexen Themas ist er nicht nützlich. Der Issue Tracker bietet kein Threading und das Antworten auf eine bestimmte Nachricht ist umständlich. Da es hier keinen wirklichen Vorschlag gibt, sondern nur eine Reaktion auf andere Vorschläge, ermutige ich Sie wirklich dringend, diese Diskussion auf die Mailingliste von golang-nuts zu bringen.

Sie können auf eine bestimmte Nachricht antworten. Ich habe gerade auf deine geantwortet. :)

Da es hier keinen konkreten Vorschlag gibt, sondern nur eine Reaktion auf andere Vorschläge,

Ein Vorschlag ist für mich ein Aufruf zur Veränderung. Dieses spezielle Thema ist Anti-Change. Schlagen Sie vor, dass wir einen Vorschlag erstellen, um die Fehlerbehandlung _nicht_ zu ändern? Ich finde das Vorschlagssystem großartig, aber es lässt den Status quo unterrepräsentiert.

nachdem ich zehn Jahre lang Java und Python geschrieben habe ... unterrichte ich neben meiner täglichen Arbeit als Softwarearchitekt auch Informatik

@kevineaton bist du fertig damit, deinen eigenen Schwanz zu lutschen?

Diese Ausgabe funktioniert als eine lang andauernde Umfrage an einem halboffiziellen Ort, an dem im Grunde jeder leicht für oder gegen Vorschläge stimmen kann.

Die Sprache nicht zu ändern, um if err != nil zu entfernen, ist ein vollkommen krummer Vorschlag, der im Grunde keine zusätzlichen Details benötigt. Ich bin mir nicht sicher, was das Problem ist. Nein, es ist nicht schrecklich lang und schwer zu groken. Das macht es nicht falsch oder schlecht oder unzureichend.

+1, wenn nichts besseres, eine gute Sache wird eine wirklich gute Stacktrace-Info sein (ohne Frames-Dance-Zeug), ich denke, x/errors bereits erreicht, aber ich würde in naher Zukunft gerne etwas Schnelleres lieben, wie Marking func s mit dem throws Schlüsselwort, das ein error + try Schlüsselwort zurückgeben würde, um Fehler-Variablenschatten zu verhindern (was ich persönlich hasse), etwa so:

func a() (int) throws {
  throw &someError{}
}

anInt, err := try a()

@icholy Das war unglaublich unangebracht. Dies ist ein Ort für Diskussionen und die Go-Community soll eine einladende Community sein. Für solche Bemerkungen ist kein Platz. Ich glaube, Sokrates hatte in einer Debatte etwas zu Beleidigungen zu sagen.

Die derzeitige Fehlerbehandlung ist anfällig für menschliche Fehler. Im Moment vergisst man leicht, err zu checken. Wenn bereits Prüfungen im Gültigkeitsbereich vorhanden sind (und dies meistens sind), wird der Compiler nicht mit unused variable . Die Fehlerbehandlung sollte streng sein - entweder _ einen Fehler machen oder ihn überprüfen - kein Beinschießen sollte möglich sein.

@kevineaton du denkst try ist kompliziert?

try ist ein Code-Geruch. Es erzwingt die Einrückung innerhalb Ihres gesamten Codeblocks, anstatt nur an einer Stelle. Darüber hinaus erzeugt die "Bubble-Up"-Natur der Ausnahmebehandlung de facto nichtdeterministisches Verhalten im Code und mehrere Austrittspunkte.

Das Schöne an der Verwendung mehrerer Rückgabewerte anstelle von try ist, dass es einen Wert gibt, der überprüft werden muss, wenn Ihre Funktion abgeschlossen ist, und einen Punkt zum Verlassen Ihrer Funktion (es sei denn, natürlich werden Guard-Anweisungen oder andere explizite Rückgaben verwendet).

try Blöcke vereiteln den ganzen verdammten Zweck mehrerer Rückgaben.

@fillest Dies würde den Code zwar etwas weniger lesbar machen, aber ich denke, dies wäre ein Mehrwert in Bezug auf Sicherheit / explizite Fehlerbehandlung. Wenn Sie sich die ursprünglichen Ziele für den Umgang mit Fehlern in Go ansehen, wäre dies meiner Meinung nach eine nette Iteration, um die von Ihnen zitierte Fehlerklasse zu vermeiden und gleichzeitig den Geist des expliziten Gutseins zu verfolgen.

Die derzeitige Fehlerbehandlung ist anfällig für menschliche Fehler. Es ist leicht zu vergessen, err im Moment zu überprüfen. Wenn bereits Prüfungen im Gültigkeitsbereich vorhanden sind (und dies meistens sind), wird der Compiler nicht mit einer unbenutzten Variablen beendet. Die Fehlerbehandlung sollte streng sein - entweder _ ein Fehler oder überprüfen Sie ihn - kein Beinschießen sollte möglich sein.

@fillest Die vorgeschlagene Änderung der Fehlerbehandlung erleichtert das "Beinschießen" und Fehler sind ausgeprägter, da sie träge behandelt werden können.

Ich habe aufgehört, Go zu verwenden, weil es an Generika, Boilerplate-Neigung, GC, Ressourcenlimits/Accounting und Workload mangelt, die von PHP-Noobs generiert werden, die nicht verstehen, was ein Compiler tut. Haskell, C# und andere haben die Fehlerbehandlung ziemlich gut gelöst ... der Go 2-Vorschlag sieht gut aus, wenn er wie zuvor eine explizite Fallbehandlung hat (unsicher).

Die Fehlerbehandlung ist das Herzstück der Programmierung. Die Modellierung der Geschäftslogik (so komplex sie auch sein mag) ist immer einfacher, als auf die ungültigen Bedingungen zu reagieren, die diese Logik erzeugt. Das einfache Weiterleiten eines Fehlers ist ein Code-Geruch. Ich wünschte, Go würde dieses Verhalten nicht fördern, sondern Fehlermanagementmuster fördern. Anfänger werden oft mit all diesem Fehlerbehandlungscode verwirrt, weil sie nicht wissen, wie zentral das Fehlermanagement ist.

Stimme voll und ganz zu, da das eingebaute try nicht dazu beiträgt, Fehler einzuschließen und ihnen auch nur für kurze Zeit Informationen hinzuzufügen.

Vor dem Umschreiben mit try :

_, err := doSomething()
if err != nil {
    return nil, errors.Wrap(err, "failed to do something")
}

_, err = doOtherThing()
if err != nil {
  return nil, errors.Wrap("failed to do the other thing")
}

Stellen Sie sich vor, was nach dem Umschreiben mit try .

Da try bereits wie eine 1-Argument-Funktion agiert, indem es ihr Argument in Klammern einschließt, könnte es ein zweites Argument akzeptieren, das den Fehlerumbruchcode darstellt.

try(extract_value(try(get_data(1), errors.Wrap(err, "failed to get data")), errors.Wrap(err, "failed to get data")))

Wobei der err implizit (auf hygienische Weise) eingeführt werden müsste. Wenn dann try als 1-Argument-Funktion verwendet wird, dann würde es nur seinen Fehler unverändert zurückgeben.

Ich stimme zu, das einzige "syntaktische Zucker"-Ding, das die Fehlerbehandlung etwas einfacher machen könnte, ist, dass wir Folgendes tun können, wenn wir mehrere Rückgaben von unseren Funktionen haben ... Unterstriche wären nur Standardwerte für alle Rückgabetypen

if err != nil {
    return _, _, err
}

@sorenvonsarvort so schlimm kommt es mir nicht vor:

var errContext string 

defer func() {
  // err is a named return
  if err != nil {
    err = fmt.Errorf("%v: %w", errContext, err)
  }
}()

errContext = "failed to do something"
_ := try(doSomething())

errContext = "failed to do other thing"
_ := try(doOtherThing())

Nach meinem Verständnis können Sie auch if err != nil { ... } wenn es für diesen bestimmten Codeabschnitt klarer ist.

try leuchtet in anderen Fällen. Stellen Sie sich eher etwas vor wie:

func trySomeComplexOp() (r result, err error) {
  a := try(step1())
  b := try(step2(a))
  c, d := try(step3(b))
  return try(lastStep(c, d)), nil
}

Code wie der obige Code kann viel sauberer sein, als wenn Sie if err != nil Blöcke einstreuen müssten. Bei Go dreht sich alles um "lineare Lesbarkeit", also denke ich, dass try Hinsicht gut abschneidet.

Es gab überwältigendes Feedback der Community, das eine optimierte Fehlerbehandlung forderte (aus der jährlichen Umfrage). Das Go-Team befasst sich nun mit diesem Problem.

Dies ist eine lautstarke Minderheit und ich wette, ein guter Teil von ihnen verwendet nicht einmal Go

@sirkon worauf stützt du diese Aussage?

@sorenvonsarvort so schlimm kommt es mir nicht vor:

Code wie der obige Code kann viel sauberer sein, als wenn Sie if err != nil Blöcke einstreuen müssten. Bei Go dreht sich alles um "lineare Lesbarkeit", also denke ich, dass try Hinsicht gut abschneidet.

In Russland nennen wir das «экономия на спичках». Verwenden Sie Google Translate, um eine Bedeutung zu erhalten.

Für diejenigen in diesem Thread, die es noch nicht getan haben, würde ich empfehlen, diesen Kommentar zum ursprünglichen try Vorschlagsproblem zu lesen. Es erörtert allgemeine Best Practices für Fehlerkontexte und wie sie mit try ausgedrückt werden können.

Ich denke, dass der Fehlerkontext in der Go-Community vielleicht ein bisschen dogmatisiert wurde. Ich weiß, dass ich persönlich darauf hereingefallen bin und meine Fehler überkontextualisiert habe, was zu sehr langen, sich wiederholenden und schwer zu lesenden Nachrichten führte. Es gibt viele Nuancen, wann Fehler kontextualisiert werden sollen und wann nicht.

Ich mag, dass try im Grunde eine Abkürzung ist und etwas Boilerplate-Code reduziert. Aber wir verlieren die Möglichkeit, die Fehler mit zusätzlichen Informationen zu umschließen. Die folgende Änderung könnte dies jedoch beheben:

f := try(os.Open(filename))

wird

f := try(os.Open(filename), "open data file")

Wenn Sie noch viel mehr tun müssen, steht Ihnen natürlich auch die "vollständige" Methode zur Verfügung, einen err != nil Check durchzuführen.

Ich stimme dem zu, aber ich werde die Bitte des Go-Teams respektieren, mehr Erfahrung mit der Änderung zu sammeln, bevor ich eine endgültige Meinung habe.

Aber meine vorläufigen Erfahrungen mit der Änderung scheinen zu bestätigen, dass sie wirklich unnötig ist. Ich habe 2 "echte" Programme mit jeweils etwa 10.000 Zeilen und führe Tryhard auf beiden Shows aus, von denen keines von dieser Änderung profitieren würde. Dies ist leicht durch die Tatsache zu erklären, dass beide Fehler immer Kontext hinzufügen. Ich habe andere kleinere "Spielzeug" -Programme in Go und tryhard hat 1 Fall gefunden, wo ich try in einem von ihnen hätte verwenden können, aber das war's.

Ich gebe zu, dass andere Leute Fehler anders behandeln als ich und ich gebe zu, dass try positiv verwendet werden kann. Der tryhard Quellcode selbst hat einige aufeinanderfolgende Fälle von return err , dass wenn er try würde, glaube ich nicht, dass die Lesbarkeit so stark beeinträchtigt würde. Aber ich fürchte nur die Missbräuche, weil diese die Lesbarkeit beeinträchtigen. Ein gutes Beispiel ist hier gegeben . Und dann zu bestimmen, was ein guter Nutzen ist oder nicht, wird eine ganz andere Geschichte sein.

Außerdem gefällt mir, dass Leute normalerweise Go-Code lesen können, auch wenn sie Go nicht selbst programmieren. Dies wird sie dazu bringen, die Magie von try zu lernen, insbesondere weil sie etwas anderes macht als die anderen try sie in anderen Sprachen gesehen haben. Dies gilt auch für neue Leute, die zu der Sprache kommen, es ist nur eine weitere Funktion, die sie in einer Sprache lernen müssen, die stolz darauf ist, einfach zu sein, indem sie "nur die Funktionen hat, die Sie brauchen".

Warten wir ab. Ich werde noch mehr mit dieser Änderung experimentieren, aber ich bin mir nicht sicher, ob sie meine Position ändern wird.

Es gab überwältigendes Feedback der Community, das eine optimierte Fehlerbehandlung forderte (aus der jährlichen Umfrage). Das Go-Team befasst sich nun mit diesem Problem.

@icholy Wie ich schon sagte, pkg/error , Ihre eigenen "Fehlerstrukturen" erstellen und/oder Methoden erstellen, um mit ihnen zu arbeiten, die je nach Implementierung auf String-Suchen zurückgreifen können. Ich würde lieber etwas sehen, das mich davor bewahrt, ganze Strukturen und Methoden zu erstellen, als Dinge, die mir bestenfalls gelegentlich ein einzelnes if sparen. Und wie bereits gesagt, können Sie es wirklich "Fehlerbehandlung" nennen, wenn diese Änderung im Grunde eine bequemere Möglichkeit bietet, den Fehler nicht wirklich zu behandeln?

Du denkst, try ist kompliziert?

Isoliert try ist nicht kompliziert, aber Sprachänderungen werden nicht isoliert betrachtet. Erwägen:

  • Erhöhte kognitive Belastung durch das Erlernen der Semantik eines neuen Builtin
  • Beeinträchtigung der Lesbarkeit durch Verwendung von try da kürzer, in Fällen, in denen stattdessen if err != nil {return ... errors.Wrap() } hätte verwendet werden sollen

Ich wiederhole die obigen Ansichten, dass Einfachheit (eine einzige Möglichkeit zum Überprüfen von Fehlern zu haben) wichtiger ist als eine kurze Möglichkeit, Fehler zu überprüfen.

Ist es nicht der Weg, einen Fehler zu behandeln, ist diese globale Anweisung und wenn dies etwas zu ignorieren ist, dann wie wird mit Paniksituationen umgegangen? Ich würde eine bessere Fehlerbehandlung unterstützen, da andere Programmierparadigmen heute mit Fehlern umgehen, aber nicht ignorieren

Ich sehe kein Problem mit dem Vorschlag von try ?
Wenn Sie altes Verhalten verwenden möchten oder Fehler auf andere Weise behandeln möchten, warum nicht die alte Methode wiederverwenden? Niemand, der dir ein Messer an den Hals drückt, du verwendest die try Syntax?
try Syntax ist einfach und eine schöne Möglichkeit, Boilerplate-Code zu reduzieren. Ich weiß nicht, warum Gopher Sadismus-Weise lieben?

Was wäre, wenn gofmt so geändert würde, dass eine Anweisung, wenn Blöcke auf einer Zeile bleiben, geändert würde?

Auf diese Weise hätten wir umschlossene Fehler in den meisten Fällen nur in einer Zeile verarbeiten können, anstatt in drei. Dies würde den von der Fehlerbehandlung eingenommenen vertikalen Platz reduzieren und den Anschein erwecken, als würde die Fehlerbehandlung nicht mehr als die Hälfte des vertikalen Platzes in der durchschnittlichen Funktion einnehmen.

So würde es aussehen:

// we already have an err in scope
err = frub.Confozzle(foo, bar, baz)
if err != nil { return errors.Wrap(err, "confozzling didn't work") }

Ich denke, dass der Fehlerkontext in der Go-Community vielleicht ein bisschen dogmatisiert wurde. Ich weiß, dass ich persönlich darauf hereingefallen bin und meine Fehler überkontextualisiert habe, was zu sehr langen, sich wiederholenden und schwer zu lesenden Nachrichten führte. Es gibt viele Nuancen, wann Fehler kontextualisiert werden sollen und wann nicht.

Die Fehlerumbruch- und Stapelrahmen-/Fehlerdrucksachen würden viel einfacher machen. Ich denke, dass es dafür mit der Zeit eine gute Lösung geben wird, aber nicht jetzt

Ich stimme voll und ganz zu, es so zu lassen, wie es ist. Es ist ein bisschen zu ausführlich, aber es ist ziemlich einfach zu folgen.

Wenn ich nur reduzieren könnte

if err := foo.x(a, b); err != nil {
    return err
}

if err := foo.y(); err != nil {
    return err
}

if err := foo.z(c); err != nil {
    return err
}

zu so etwas wie

if err := check foo.x(a, b), foo.y(), foo.z(c); err != nil {
    return err
}

wäre vielleicht auch toll, ohne die Redewendung IMHO zu sehr zu ändern.

@henvic Ich denke, das Problem dabei ist, dass Sie davon ausgehen, dass Sie die drei Fehler auf die gleiche Weise behandeln möchten. Go zwingt Sie dazu, darüber nachzudenken, wie Sie jeden Fehler einzeln behandeln können. Dies wird durch separate Fehlerprüfungen deutlich. Wenn man sie zu einem zusammenfasst, muss der Entwickler kognitiv zurückgehen und prüfen, ob diese Fehler _wirklich_ auf die gleiche Weise behandelt werden sollten? Ich denke, mit diesem Vorschlag verlieren wir die Klarheit und erzwungenes Umdenken von Fehlern für ein paar Tastendrücke weniger.

@sanborn , du hast recht. Ich stimme zu.

Dieser check könnte auch ein checkNonZero Operator sein, der nur ein Rückgabeargument annehmen und beim ersten Wert ungleich Null (wie null) zurückgeben würde. Aber abgesehen davon, dass es zu vage ist und die Sprache noch mehr beeinflusst. Es würde sogar nur einen kleinen Hinweis darauf geben, dass Sie es nicht verwenden sollten, wenn Sie beispielsweise io.EOF erwarten. Eine andere Möglichkeit wäre vielleicht, sich auf go vet zu verlassen, um Sie zumindest über die häufigsten Fälle zu informieren (wie io.EOF beim Lesen aus einer Pfeife) ... aber diese Idee hört sich nicht gut an alles für mich.

Ich sehe kein Problem mit dem Vorschlag von try ?
Wenn Sie altes Verhalten verwenden möchten oder Fehler auf andere Weise behandeln möchten, warum nicht die alte Methode wiederverwenden? Niemand, der dir ein Messer an den Hals drückt, du verwendest die try Syntax?
try Syntax ist einfach und eine schöne Möglichkeit, Boilerplate-Code zu reduzieren. Ich weiß nicht, warum Gopher Sadismus-Weise lieben?

Wir leben alle in Gemeinschaft. Jeden Tag arbeite ich mit viel Code, der von anderen Leuten geschrieben wurde. Eine Änderung der Sprache wird mich also beeinflussen, auch wenn ich sie nicht benutze.

In großen Projekten werden die schlimmsten Protokolle von einer Funktion generiert, die wir vergessen haben, dass jemand geschrieben hat, die eine Bibliothek aufruft, die eine Bibliothek aufruft, die eine Bibliothek aufruft, die ein generisches new Exception() ausgibt, das nichts abgefangen hat, bis ein "Pokemon"-Ausnahmehandler dies getan hat und protokollierte einen generischen Fehler. Die zweitschlimmsten sind die gleichen, aber mit einem undurchschaubaren mehrere hundert Zeilen langen Stack-Trace, mit dem wir wahrscheinlich die Ursache herausfinden können (zum Glück findet man die meisten relevanten Informationen, wenn man nur nach github.com/<us>/<ourproject> sucht, aber da ist manchmal viel). Trotz ihres Namens sind "Exceptions" in großen Java-Projekten erschreckend wenig außergewöhnlich.

Selbst wenn es viel redundanten Kontext gibt, waren einfache Go-Fehlerzeichenfolgen wie "narf: Error unpoiting the zort: foo: Unexpected bar in baz: {\"ork\": \"morpork\"}" (meiner Erfahrung nach) normalerweise sehr einfach zu interpretieren, solange wir sorgfältig darauf geachtet haben, den wichtigen Kontext irgendwo einzubetten der tatsächliche Fehlerwert. Wenn sich herausstellt, dass wichtiger Kontext fehlt, ist das _auch_ normalerweise ziemlich offensichtlich. Der "Fix" in diesen Fällen besteht darin, mehr Kontext hinzuzufügen und auf einen weiteren Fehler zu warten, also ist es nicht perfekt, aber im Großen und Ganzen bevorzuge ich dies immer noch, als durch Stack-Traces zu scrollen und / oder mich auf die Abhängigkeiten der Abhängigkeiten meiner Abhängigkeiten zu verlassen, um "werfen" " oder "raise" vernünftige Fehlermeldungen. Ich weiß es wirklich zu schätzen, wie der Name panic() die meisten Go-Entwickler daran hindert, so großzügig die im Wesentlichen dieselbe Sprachfunktion einzusetzen. Lass uns error und panic dasselbe machen.

Ich bin gelegentlich in Situationen geraten, in denen eine Funktion etwa ein Dutzend Fehlermodi hatte und die überwiegende Mehrheit davon dieselbe Fehlermeldung verdiente. Die Wiederholung stört mich nicht wirklich, aber normalerweise gibt es jemanden in meinem Team, den es stört, also gehen wir Kompromisse ein, indem wir zu Beginn der Funktion eine Schließung deklarieren, um diese häufigen Fehler zu behandeln.

func foo(a, b, c SomeArgType) (x, y, z SomeReturnType, err error) {
  handleError := func(handleErr error) (x, y, z SomeReturnType, err error) {
    log.WithFields(logrus.Fields{
      "package": "foo",
      "func": "foo",
      "arguments": map[string]SomeArgType{"a": a, "b": b, "c": c},
      "error": handleErr,
    }).Error("Error fooing the bar")
    return reasonable, default, values, handleErr
  }

  err := doABunchOfThings()
  if err != nil {
    return handleError(err)
  }
}

Was zugegebenermaßen in gewisser Weise _noch_ eine unvollkommene Lösung ist. Aber ich mag es, dass es dadurch für zukünftige Entwickler sehr einfach ist, immer noch zu verstehen, wann und was foo zurückgibt, ohne dass der Kontrollfluss zu sehr herumspringt.

Wenn dieses Wiederholungs-"Problem" in zahlreichen Paketen extrem häufig vorkommt, anstatt (wie ich es normalerweise sehe) auf eine Handvoll irreduzibel-komplexer Funktionen in einem irreduzibel-komplexen Paket beschränkt zu sein, könnte wahrscheinlich ein projektweiter "Functor" verwendet werden, um ein ähnliches Ende, und (seufz) wenn ein Konzept parametrischer Typen in die Sprache aufgenommen wird, könnten Sie noch mehr Details hinter einer Fehlerbehandlungs-Factory-Factory verstecken, ohne sich auf try/catch verlassen zu müssen.

@thomasf

Die Fehlerverpackung und das Stapelrahmen-/Fehlerdrucken würden viel einfacher machen

Ich stimme zu.

Persönlich würde ich es vorziehen, mit der Einführung leistungsfähigerer go2-Funktionen zu warten, bis der parametrische Polymorphismus geklärt ist, da dies möglicherweise beim Design der restlichen Funktionen nützlich sein kann

Ein paar Leute im ursprünglichen try Vorschlag diskutierten auch das Warten auf Generika, aber mir ist nicht klar, wie der parametrische Polymorphismus den try Vorschlag anders machen würde. Es ist bereits integriert, also nicht auf die Grenzen dessen beschränkt, was wir in der Sprache ausdrücken können.

@icholy

Es gab überwältigendes Feedback der Community, das eine optimierte Fehlerbehandlung forderte (aus der jährlichen Umfrage). Das Go-Team befasst sich nun mit diesem Problem.

Nur um darauf zu antworten; Auch in Großbritannien gab es eine Mehrheit für den Brexit. Sicher, die EU bringt auch einige Nachteile mit sich, auf die die Öffentlichkeit reagiert hat. Nachdem jedoch alle Alternativen angesprochen wurden, schien es, als wäre ein Verbleib in der EU doch nicht so schlimm.

Nun, es ist keineswegs meine Absicht, dies zu politisieren, und Sie können dem oben Gesagten nicht zustimmen. Was ich aber zeigen möchte, ist, dass auch wenn eine Mehrheit zunächst etwas als störend empfindet, es nach Prüfung aller Alternativen die beste Lösung sein kann.

Ich halte nichts von der Fehlerbehandlung, aber es _könnte_ ein Argument dafür sein, die Dinge so zu belassen, wie sie sind.

In einer professionellen Codierungsumgebung nutzen wir aktuelle Fehlerbehandlungspraktiken, um Tracing-Systeme mit Anmerkungen zu versehen und Protokolle zu dekorieren. Abgesehen davon ähnelt die implizite Rückgabe der Verwendung von Panik in einer exportierten Bibliotheksfunktion, da sie die unmittelbare Lesbarkeit der Flusskontrolle verdeckt.

@icholy

Es gab überwältigendes Feedback der Community, das eine optimierte Fehlerbehandlung forderte (aus der jährlichen Umfrage). Das Go-Team befasst sich nun mit diesem Problem.

Nur um darauf zu antworten; Auch in Großbritannien gab es eine Mehrheit für den Brexit. Sicher, die EU bringt auch einige Nachteile mit sich, auf die die Öffentlichkeit reagiert hat. Nachdem jedoch alle Alternativen angesprochen wurden, schien es, als wäre ein Verbleib in der EU doch nicht so schlimm.

Sie müssen die Behauptung dieser Person nicht ernsthaft in Betracht ziehen: Die Anzahl der Emojis zeigt, dass die Leute im Allgemeinen try Vorschläge nicht mögen und sie im Allgemeinen diesen „Lass es so wie es ist“-Vorschlag mögen.

PS in meiner Praxis hat eine große Mehrheit der Leute, die Go in seiner Hauptdomäne (Netzwerkdienste, CLI-Dienstprogramme) nicht mögen, es nicht einmal verwendet. Daher würde ich ihre Meinungen lieber ignorieren.

Wir brauchen bessere, weniger umstrittene Optionen als den try Vorschlag.
Ich sehe keine Dringlichkeit für übereilte Lösungen.

@velovix Ich glaube, ich hasse parametrischen Polymorphismus mehr als Try/Catch-Fehler "Handling", aber wenn es zu einer Sprachfunktion würde, könnte ich einige Möglichkeiten sehen, wie es die Notwendigkeit einer weiteren integrierten Sprachfunktion umgehen würde.

Zum einen, wenn der Code, den die Leute nicht wiederholen möchten, ein Musterbeispiel ist:

foo, err := Foo()
if err != nil {
  log(err)
}
bar, err := Bar(foo)
if err != nil {
  log(err)
}
// ...

Dann würde eine Kombination aus parametrischen Funktionen, Typinferenz und Objektentwurfsmustern im _vielleicht_ oder _optionalen_ Stil die Boilerplate einfach (heh) reduzieren, ohne auf seltsame nichtlineare Kontrollflussstrategien zurückzugreifen:

func<T> DoWithErrorLogging(f func(any...) (T, error), args... any) T {
  t, err := f(args...)
  if err != nil {
    log(err)
  }
  return t
}
// ...
foo := DoWithErrorLogging(Foo)
bar := DoWithErrorLogging(Bar, foo)

IMO wäre das alles viel, viel schlimmer als Go1. Aber besser als diese _plus_ try/catch-Schlüsselwörter in der Sprache zu haben.

Ehrlich gesagt ... So wie die Dinge im Moment stehen, denke ich, dass meine Lieblingsänderungen für Go2 nur all die kleinen Unannehmlichkeiten in Go1 beheben würden, wie zum Beispiel die net/http Standardeinstellungen, die veränderliche gemeinsame Globals verschachtelt in veränderlichen gemeinsamen Globals sind (nur machen Hashicorps cleanhttp im Grunde zum Standard) oder (*time.Timer).Reset() mit einem nutzlosen Rückgabewert , den Sie nur kennen müssen, oder das gesamte syscall-Paket . Go3 kann fast unmittelbar danach mit allen Tumoren freigesetzt werden, die Menschen darauf wachsen lassen wollen; Ich verstehe nicht, warum kleine und große Breaking Changes alle in einer einzigen Version durchgeführt werden müssen.

Ich bin für try ... bei sparsamer Verwendung. Ich vermute, dass populäre Projekte Richtlinien hinzufügen, wann / ob sie mit der Verwendung von try in Ordnung sind, und dass kleine / neue / Einpersonenprojekte manchmal unter schlechten Fehlern - und damit mangelnder Verwendung - leiden werden, aufgrund von try zu oft. Diese Projekte werden absterben oder fixiert oder gegabelt werden.

Ich finde das Hinzufügen von try zur Sprache wirklich nicht so schrecklich. Wenn sich die schlimmsten Befürchtungen der Menschen als begründet erweisen, wird ihr Einsatz verpönt. Unerfahrene Leute werden es wahllos verwenden, während andere dies nicht tun. Es ist nicht das Ende der Welt.

Wenn try hinzugefügt wird, werde ich es wahrscheinlich in einigen Fällen verwenden. Diese Fälle sind, in denen ein Fehler zurückgegeben wird, aber ich denke, dass es so unglaublich unwahrscheinlich ist, dass tatsächlich ein Fehler auftritt, dass ich keinen Sinn darin sehe, Kontext hinzuzufügen, und ich den Fehler einfach so zurückgebe, wie er ist. Wenn ich zum Beispiel gerade ein Dateisystem erstellt habe, das eine Festplatte ausfüllt, von der ich weiß, dass sie 1 TB groß ist, kann ich sicher sein, dass genügend Platz vorhanden ist, um eine 1-KB-Datei oder ein Verzeichnis zu erstellen. Wenn dies fehlschlägt, möchte ich den Fehler nicht ignorieren - er könnte auf einen Fehler an anderer Stelle, einen Hardwarefehler usw. hinweisen. Es lohnt sich jedoch nicht wirklich, sich darum zu bemühen, jeden wahnsinnig unwahrscheinlichen Fehler zu kommentieren.

Ich mag es nicht, wie try(..) nur ein weiteres Paar Klammern/Klammern auf den Gedankenstapel eines Programmierers setzt, um beim Tippen daran zu denken. Und für die längste Zeit, die ich mir vorstellen kann!

Also das ist besser:
Wert, err := foo()
err zurückgeben, wenn err != nil

Aber das ist immer noch so üblich. Also ich würde gerne, wenn smt wie folgt irgendwie möglich sein könnte:

Wert, Fehler überprüfen := foo()

Wenn Go eine lesbare Sprache sein möchte, sollte die Fähigkeit, schwer zu lesende oder zu verstehende Gedanken zu machen, minimiert werden.

Wenn Go eine gute Fehlerbehandlung haben möchte, sollte es dazu beitragen, dass Fehler zusätzlichen Kontext haben, wenn sie in der Aufrufliste aufsteigen. Die Anforderung, Verzögerungen zu verwenden, um Fehler zu behandeln, erscheint verwirrend. Was ist, wenn Ihr Fehlerhandler einen Fehler hat? Defers werden in Stack-Reihenfolge ausgeführt, müssen wir Handler in umgekehrter Reihenfolge deklarieren?

Wenn Aussagen einfach sind und hier wenig Raum für Mehrdeutigkeiten lassen. Ich habe das Gefühl, dass try eher ein Problem löst als ein echtes technisches Problem. Ich mag diesen Vorschlag, weil er es der schweigenden Mehrheit ermöglicht, endlich eine Erklärung abzugeben, ohne jede Facette dieser komplexen Vorschläge vollständig zu verstehen.

@icholy Bitte sei höflich. Bitte beachten Sie den Gopher-Verhaltenskodex: https://golang.org/conduct. Vielen Dank.

Alle: zusätzlich zu meinem Kommentar oben (https://github.com/golang/go/issues/32825#issuecomment-506740412) bitte https://golang.org/wiki/NoPlusOne beachten

@sanbornm

(Ich stimme zu, dass es möglich ist, auf eine Nachricht zu antworten; ich sagte, es sei umständlich, nicht unmöglich. Und mein Standpunkt zum Einfädeln bleibt bestehen, da dieser Mini-Thread in einem Schneesturm anderer Kommentare verloren geht.)

Ein Vorschlag ist für mich ein Aufruf zur Veränderung. Dieses spezielle Thema ist Anti-Change. Schlagen Sie vor, dass wir einen Vorschlag erstellen, um die Fehlerbehandlung nicht zu ändern? Ich finde das Vorschlagssystem großartig, aber es lässt den Status quo unterrepräsentiert.

Es ist nicht notwendig, Vorschlag A zu erstellen, der besagt, dass Vorschlag B nicht angenommen werden soll. Stimmen Sie stattdessen Vorschlag B ab. Für detaillierte Diskussionen zu Vorschlag B verwenden Sie diesen Vorschlag oder die Mailingliste.

(Ich verstehe, dass Vorschlag B in diesem Fall gesperrt ist; die Tatsache, dass dieser Vorschlag in weniger als einem Tag 77 Kommentare enthält, zeigt, warum. Diese Diskussionsebene funktioniert einfach besser auf einer Mailingliste als auf dem Issue-Tracker.)

@ianlancetaylor

(Ich stimme zu, dass es möglich ist, auf eine Nachricht zu antworten; ich sagte, es sei umständlich, nicht unmöglich. Und mein Standpunkt zum Einfädeln bleibt bestehen, da dieser Mini-Thread in einem Schneesturm anderer Kommentare verloren geht.)

Fair genug, das macht Sinn. Mailinglisten sind großartig, aber ich persönlich finde es in diesem Fall einfacher, über GitHub beizutragen. Ich habe nicht viel zu sagen, außer dass die aktuelle Fehlerbehandlung großartig ist und ich wünsche, dass es so bleibt. Emoji/Stimmen eignen sich dafür hervorragend. Sie möchten wahrscheinlich nicht, dass 100 Leute "Bitte lassen Sie die Fehlerbehandlung in Ruhe" in die Mailingliste, wo 100 "Stimmen" ausreichen würden.

Da dieses Thema gesperrt ist, kann es nicht mehr mit Emojis "abgestimmt" werden. Deshalb glaube ich, dass dieses Problem überhaupt erst geschaffen wurde.

Ein Nebenpunkt, aber damit verbunden, wurde das Abhängigkeitsmanagement nicht gut gehandhabt. Dep hat super funktioniert und Go Mod wurde (wie es schien) aus dem Nichts gewählt [1]. Ich verstehe es, deshalb wurde das Vorschlagssystem geschaffen. Ich habe nur das Gefühl, dass das Vorschlagssystem in diesem Fall die Community unterrepräsentieren könnte, wenn Probleme geschlossen sind und wir aufgefordert werden, zu Mailinglisten zu gehen.

[1] https://twitter.com/_rsc/status/1022588240501661696

Bearbeiten: Das Go-Team und die Community leisten größtenteils hervorragende Arbeit, indem sie auf das Community-Feedback hören. Ich schätze die ganze Arbeit, die darin steckt. Die Go-Umfragen sind ein großartiges Beispiel.

@sanbornm

Dep hat super funktioniert

Muss hier widersprechen. Go-Module haben endlich das unbekannte „gobindata“-Problem mit ihrem persistenten Caching von https://proxy.golang.org gelöst

Dieser Dep-Typ erkannte das Problem nicht einmal und spielte stattdessen mit der ausgefallenen "Versicherung" über VCSes.

@sirkon Dies ist ein wenig vom Thema

So wie es aussieht, würde ich die Dinge lieber so lassen, wie sie sind, es sei denn, es würden weitere Einschränkungen hinzugefügt, wie z. B. 1 try-Anweisung pro Zeile. Der Grund ist, dieses Beispiel aus dem Vorschlag zu betrachten - es scheint harmlos genug zu sein, info := try(try(os.Open(file)).Stat()) aber es verliert Dateihandles, die über den Rahmen des normalen Kontrollflusses hinausgehen. Ich denke, wir werden bei io.Closer Implementierungen oder anderen Bereinigungsfunktionen eine Zunahme von Dateiressourcenlecks sehen, die Leute bei der Suche nach kompakteren Code umgehen können.

Vielleicht werden einige Leute es für belanglos halten, weil f nicht mehr live und somit sofort für GC in Frage kommt und irgendwann der Finalizer dafür sorgt, dass f geschlossen wird. Ich denke, dass es die früheren klaren (von Linter unterstützten) Konventionen zur Verwendung von Defer heute ändert, die an einen Funktionsblock gebunden sind. Beim Verlassen des Funktionsbausteins wird die Ressource freigegeben. Das Vertrauen auf den Garbage Collector bietet keine Garantie dafür, dass Sie die Ressourcen nicht erschöpfen (typische Standardwerte für das Open File Handle-Limit können zwischen 1k und 4k liegen) - was leicht mit einem einfachen filepath.Walk überschritten wird, der die Dateien, die es statisiert, nicht schließt.

Zusammenfassend denke ich, dass diese Syntax, wie sie implementiert ist, ein subtiles Risiko bei der Ressourcenverwaltung in Go darstellt, da ihr der ctor/dtor fehlt und sie sich auf eine untergeordnete GC-Maschinerie verlässt, die weit von Codeblöcken entfernt ist, um Ressourcenlecks zu verhindern. Etwas, das harmlos erscheint, in einen potenziellen Fehlerzustand umwandeln (zu viele geöffnete Dateien).

var n int
for _, name in try(os.Readdir(...)) {
   n += try(getSize(name))
}
func getSize(name string) (int, error) {
   return try(try(os.Open(name)).Stat()).Size
}

bearbeiten:
Was die Einschränkungen angeht, denke ich, dass es besser wäre, als 1 pro Zeile zu sagen, wenn es nur auf der rechten Seite einer Zuweisung gültig wäre, da a, b := try(get("a")), try(get("b")) vernünftig genug ist. Aber es lässt immer noch die Möglichkeit, try(os.Open(name)).Stat() zu tun - was, wenn Sie try() ungültig machen würden, aber nur, wenn Sie sich nicht auf der rechten Seite einer Aufgabe befinden, bleiben Sie mit etwas zurück, das nicht sehr funktioniert wie at alle.

@cstockton wow tolles Ergebnis!

Rust hat tatsächlich ein ähnliches Makro ( ? wenn ich mich richtig erinnere), das genau das tut, was dieses try tun sollte, aber sie haben dort die richtigen Raii, also ist es in dieser Sprache kein Problem und ein riesiges Loch in unserem Fall

@sanbornm ja, die Hälfte des Internets in Ihrem Repo zu behalten, sieht in der Tat nach einer großartigen Idee aus.

Finishing Touch

Da jemand ein Dienstprogramm erwähnte

| Projekt | LOC* | Kandidaten ausprobieren |
|------------|------|----------------|
| cal1 | 2047 | 3 |
| Pumpe1 | 1030 | 0 |
| docs1 | 4576 | 8 |
| Hugoutil | 604 | 1 |
| alles andere | 8452 | 23 |

  • Nur Go-Code, Kommentare ausgenommen, gemäß dem Dienstprogramm cloc .

Denken Sie daran, dass der Inhalt von "alles andere" schnelle Hacks und Code enthält, die ich geschrieben habe, als ich Go lernte.

Meine allgemeine Schlussfolgerung ist, dass zumindest für mich der Vorschlag von try nicht dazu beitragen würde, meine Fehlerbehandlung in einem sinnvollen Maße zu rationalisieren.

Der Hauptgrund, warum ich go liebe, ist, dass seine Spezifikation Coder auf eine kleine Teilmenge der Syntax beschränkt, die für andere Sprachen verfügbar ist. Da es sich um ein so kleines Feature-Set handelt, ist es einfach, das gesamte Feature-Set zu erlernen. Ein zukünftiger Entwickler kann sich wahrscheinlich meinen Code ansehen und wissen, was ich getan habe. Jedes neue Ding, das der Sprache hinzugefügt wird, verringert die Wahrscheinlichkeit, dass zukünftige Entwickler dieses Ding kennen. Das Extrem der rutschigen Steigung ist eine Sprache, deren Komplexität das Groken erschwert, wie C++ oder Scala.
Ich möchte keine Syntaxergänzungen für go 1 sehen. Setzen Sie sie stattdessen in go 2 ein.

@miekg bitte fügen Sie diesen Link https://github.com/golang/go/issues/32825#issuecomment -506882164 in den Vorschlag ein. Das Beispiel disqualifiziert die ganze Idee dieses neuen try Schlüsselworts vollständig.

image

Ich stimme voll und ganz zu, es so zu lassen, wie es ist. Es ist ein bisschen zu ausführlich, aber es ist ziemlich einfach zu folgen.

Wenn ich nur reduzieren könnte

if err := foo.x(a, b); err != nil {
  return err
}

if err := foo.y(); err != nil {
  return err
}

if err := foo.z(c); err != nil {
  return err
}

zu so etwas wie

if err := check foo.x(a, b), foo.y(), foo.z(c); err != nil {
  return err
}

wäre vielleicht auch toll, ohne die Redewendung IMHO zu sehr zu ändern.

Wenn Sie von einem "Vielleicht"-Typ sprechen, erfordert er zuerst einen Variantentyp.

Lassen Sie uns if err != nil beibehalten, es funktioniert, es ist klar, es ist wirklich nicht so ausführlich und es macht im Fluss des Codes Sinn. Wenn Sie Code mit diesem Konstrukt lesen, wissen Sie, was es tun wird.
Behalten wir es, fügen wir nicht try

Wenn ich Code lese, möchte ich, dass die Zeilen, die die Arbeit erledigen, klar lesbar sind, ohne oder mit einem Minimum an Fehlerbehandlung.

Die 3 Buchstaben 'err' auf gleicher Ebene sind für mich akzeptabel. Ich möchte keine 'Check'-Funktion, die den wichtigen Code umschließt, weil der wichtige Code auf einer zweiten Ebene wäre (erinnern Sie sich an Lisp?), und ich möchte nicht eine Zeile vorher 'versuchen', weil der wichtige Code eingerückt wäre und in der zweiten Zeile.

res, err := begin_job()
wenn err != nil {
handle_error()
}

err = continue_job(res)
wenn err != nil {
handle_error()
}

Mit diesem Code können Sie den Ablauf des Nicht-Fehlerfalls lesen, indem Sie die ersten Zeilen von Blöcken lesen (wie ich die Titel von Dokumenten lese, wenn ich sie schnell lesen muss).

Da jemand ein Dienstprogramm erwähnte

Projekt LOC* Kandidaten ausprobieren
cal1 2047 3
Pumpe1 1030 0
docs1 4576 8
Hugoutil 604 1
alles andere 8452 23

  • Nur Go-Code, Kommentare ausgenommen, gemäß dem Dienstprogramm cloc .

Ich denke, dass try in größeren Programmen mehr benötigt wird. Wenn ich nur aus dem Speicher zeichne, denke ich, dass Programme mit LOC-Größen um 15-20k und aufwärts sie mehr benötigen, da dann möglicherweise Layer erhalten werden, die nur Fehler weitergeben müssen, da sie in einem geschlossenen System von geeignet spezifiziert und behandelt werden sowohl auf der sendenden als auch auf der empfangenden Seite. Es hängt jedoch stark davon ab, um welches Programm es sich handelt. Ich würde try viel wahrscheinlich auch in kleineren Programmen nicht verwenden

Ich denke, dass Try in größeren Programmen mehr benötigt wird.

Guter Punkt. Ich habe tryhard auf heptio/contour ausprobiert, 28.7k Zeilen Quelltext, tryhard fand 12 Ersetzungen.

Ich denke, dass Try in größeren Programmen mehr benötigt wird.

Guter Punkt. Ich habe tryhard auf heptio/contour ausprobiert, 28.7k Zeilen Quelltext, tryhard fand 12 Ersetzungen.

BEEINDRUCKEND! 12 vs 28,7K Zeilen, dies erfordert wirklich ein dediziertes Schlüsselwort!

Nun, ich bin mehr daran interessiert, Ihre POV auf diese :

stat := try(try(os.Open(fileName)).Stat())

Ich denke, es ist üblicher, wenn Ihr Programm etwas monolithischer ist und nicht Teil einer Dienstintegration zwischen vielen Diensten ist. Wenn ich in diesem Repository ( heptio/contour ) einfach nach fmt.errorf oder errors in github suche ( heptio/contour ), gibt es nur sehr wenige Ergebnisse, so dass es schwierig ist, einen schnellen Überblick zu bekommen.. Aber wie ich sagte, dass es wahrscheinlich von Programm zu Programm sehr unterschiedlich ist, selbst bei größeren Programmen.

Angenommen, Sie haben ein einzelnes Programm, das nicht viele externe Bibliotheken verwendet. Dann können Sie einen bestimmten AuthorizationError haben (und Sie wissen, dass alle zurückgegebenen Fehler spezifisch genug sind mit allen bereits behandelten und umschlossenen io-Fehlern), die bereits Ihre Benutzer-Metadaten enthalten und ohne Änderung einiger Ebenen ohne große Änderungen an Dingen, die tatsächlich benötigt werden, weitergegeben werden können um sie bis zur Anforderungsschicht zu behandeln.

Ich denke, es ist üblicher, wenn Ihr Programm etwas monolithischer ist und nicht Teil einer Dienstintegration zwischen vielen Diensten ist. Wenn ich nur nach fmt.errorf oder errors in diesem Repository in Github suche, gibt es nur sehr wenige Ergebnisse, daher ist es schwierig, einen schnellen Überblick zu bekommen.. Aber wie gesagt, es variiert wahrscheinlich stark vom Programm zu programmieren, auch für größere Programme.

Angenommen, Sie haben ein einzelnes Programm, das nicht viele externe Bibliotheken verwendet. Dann können Sie einen bestimmten AuthorizationError haben, der bereits Ihre Benutzer-Metadaten enthält und ohne Änderung ein paar Schichten ohne große Änderungen an Dingen, die sie tatsächlich behandeln müssen, bis zur Anforderungsschicht weitergegeben werden kann.

Du hast keine Ahnung. Anmerkungen dienen dazu, leicht einen Weg zu finden, auf dem ein Fehler aufgetreten ist. Wir haben ähnlich os.NotExist aber dies ist kaum ein guter Hinweis auf den Fehlerpfad.

@thomasf hier ist ein weiterer Datenpunkt aus einer mehrere Jahre alten Arbeitskopie von juju/juju,

529628 Quelllinien, tryhard fand 1763 (0,3%) Ersatz.

Ja sicher. Da Sie sich mit beiden beschäftigt haben, sind sie jedoch wahrscheinlich keine großartigen Beispiele für verschiedene Arten, Programme zu schreiben. Ich habe nicht einmal die Zeit, das Tryhard-Programm atm auszuprobieren und noch weniger es mit verschiedenen Quellen richtig auszuführen (was sowieso unmöglich zu sammeln sein könnte, da geschlossener Quellcode weggelassen wird, wenn er über github gesammelt wird).

529628 Quelllinien, tryhard fand 1763 (0,3%) Ersatz.

Wie jemand (Zitat erforderlich) weise gesagt hat, macht try es nicht einfacher, mit Fehlern umzugehen. Es macht es einfacher, sie nicht zu handhaben.

Wenn Sie Code analysieren und viele try Ersetzungen finden, sagt Ihnen nur, dass der Code nichts mit Fehlern macht, außer sie zurückzugeben. Das ist wahrscheinlich kein großartiger Code. Sollten wir es den Leuten erleichtern, faul zu sein und sich keine Sorgen über Fehler zu machen? Ist das nicht ein Grund, warum Go keine Ausnahmen hat, um genau das zu vermeiden?

Dazu werde ich keine Stellung nehmen. Was ich jedoch finde, sind ihre kleinen unterstützenden Beweise, die darauf hindeuten, dass

A. Es gibt viele Orte, an denen try auf vorhandene go-Codebasen anwendbar ist
B. Die Fehlerbehandlung im Allgemeinen macht einen erheblichen Teil von SLOC aus, basierend auf meinen eigenen Messungen und den vom Go-Team diskutierten Zahlen, siehe https://youtu.be/RIvL2ONhFBI?t=440 Timecode 07:26

Wenn Sie Code analysieren und viele try Ersetzungen finden, sagt Ihnen nur, dass der Code nichts mit Fehlern macht, außer sie zurückzugeben. Das ist wahrscheinlich kein großartiger Code. Sollten wir es den Leuten erleichtern, faul zu sein und sich keine Sorgen über Fehler zu machen? Ist das nicht ein Grund, warum Go keine Ausnahmen hat, um genau das zu vermeiden?

  1. Sie fällen Werturteile über theoretischen Code, von dem Sie nichts wissen, was keine gute Angewohnheit ist.
  2. Ich sehe nicht, dass try einfacher zu missbrauchen ist als das, was wir jetzt haben, go hat keine Funktionen, um die Fehlerbehandlung zu erzwingen, und es ist sehr einfach, es bereits zu überspringen. Für mich geht es bei try darum, Code leichter lesbar zu machen, WENN er keine Fehler verarbeiten muss.

Vielleicht brauchen wir try weil es an vielen Stellen nicht benötigt wird, aber lassen Sie uns die Idee unterhalten und Anwendungsfälle entwickeln, anstatt nur mürrisch darüber zu sein ...

Ich kann mir selbst nicht viele praktische Situationen vorstellen, wie ich es verwenden würde, aber es existiert auch noch nicht, daher ist es schwer zu sagen, ob ich einige Dinge anders gestalten würde, wenn es existieren würde do gruppiert eine Reihe von Aktionen in einer anonymen Funktion wie dieser, indem Sie try verwenden und den Fehler trotzdem behandeln, bevor Sie ihn an den Aufrufer zurückgeben. Es könnte etwas Code viel lesbarer machen.

var v1, v3 string
if err := func() error {
    try(onething())
    v = try(twothing())
    try(otherthing())
    v3 = try(somethingg())
}(); err != nil {
  ... handle error...
}

Ich denke, es könnte an dieser Stelle eine gute Idee sein, eine Website zu schreiben, um Daten für tryhard auf verschiedenen Paketen zu speichern und diese zu visualisieren. Vielleicht kann ein wenig golang/gddo (godoc.org) die Arbeit erledigen.

Ich lasse if err != nil lieber in Ruhe. Aber wenn wir etwas zur Fehlerbehandlung hinzufügen müssen, ist hier ein neuer Vorschlag, der das Schlüsselwort throws hinzufügt.

32852

Ohne einige der hier bereits dargelegten Argumente zu wiederholen, stimme ich dem Gefühl zu, if err != nil so zu

Die Perspektive, die ich anbieten kann, ist folgende: Als jemand, der Hunderten von Neulingen Go beigebracht hat (sowohl in der Programmierung als auch in Go aus anderen Sprachen), war if err != nil für sie nie ein Problem. Die erfahrenen Programmierer in meinen Workshops finden das zunächst ungewohnt, lernen aber schnell die Explizitheit der Fehlerbehandlung in Go zu lieben.

Es gibt größere Bedenken, die wir in der Sprache ansprechen können, und die klare Reaktion der Community auf dieses Problem sagt, dass if err != nil nicht dazu gehört.

Go ist aus so vielen Gründen perfekt. Die wichtigste davon ist „if err != nil“. Es mag ausführlich erscheinen, aber für Leute, die Programmieren lernen, ist es einfacher, Ihren Code zu debuggen und Code zu korrigieren.

@davecheney

Dazu werde ich keine Stellung nehmen. Was ich jedoch finde, sind ihre kleinen unterstützenden Beweise, die darauf hindeuten, dass

A. Es gibt viele Orte, an denen try auf vorhandene go-Codebasen anwendbar ist
B. Die Fehlerbehandlung im Allgemeinen macht einen erheblichen Teil von SLOC aus, basierend auf meinen eigenen Messungen und den vom Go-Team diskutierten Zahlen, siehe https://youtu.be/RIvL2ONhFBI?t=440 Timecode 07:26

Ich befürchte, dass im gegenwärtigen Klima alle Beispiele, die wir finden, einfach als "Nun, das ist wahrscheinlich kein guter Code" abgetan werden.

Hier ist ein Beispiel:

llorllale:~/go/src/github.com/hyperledger/fabric$ cloc --exclude-dir=vendor .
    2406 text files.
    2256 unique files.                                          
    3130 files ignored.

http://cloc.sourceforge.net v 1.60  T=6.69 s (272.8 files/s, 58350.9 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
Go                             1751          54365          34149         294005
YAML                             35            547           2171           2060
Bourne Shell                     26            354            325           1312
make                              3            135             96            418
CSS                               1             40             14            140
HTML                              3              7              5             63
Python                            1             50            103             57
Bourne Again Shell                1              1              6             50
Java                              3              7              4             26
XML                               2              1              4              2
--------------------------------------------------------------------------------
SUM:                           1826          55507          36877         298133
--------------------------------------------------------------------------------
llorllale:~/go/src/github.com/hyperledger/fabric$ tryhard -l . | grep -v vendor | less | wc -l
1417

Fairerweise muss gesagt werden, dass die Daten über die Anzahl der von Tryhard gefundenen Standorte durch Konventionen verwechselt werden können, die Umbruchfehler erfordern. Zum Beispiel, wenn Ihre Firmenversammlung zu

if err != nil {
   return errors.Wrap(err) 
} 

...
if err != nil {
   return errgo.Notef(err, "error doing x") 
} 

das würde von tryhard nicht gemeldet werden.

Wir haben eine solche Convention in meiner Firma. Wenn ich eine einfache Suche und Ersetzung durchführe, um diese auf die nackten Fehler zurückzugeben, bekomme ich folgende Ergebnisse:

Language                             files          blank        comment           code
---------------------------------------------------------------------------------------
Go                                    2488          40317          15901         297038

tryhard meldet 2736 Ersetzungen, aber eine manuelle Überprüfung der verbleibenden Umhüllung sieht so aus, als würde dies um etwa 1850 unterzählt, daher würde ich insgesamt ~4500 try Verwendungen in unserer Codebasis von 300.000 Zeilen schätzen.

(Persönlich bin ich für die aktuelle Explizitheit der Fehlerbehandlung und habe nichts dagegen.)

Zum Beispiel, wenn Ihre Firmenversammlung zu
[Fehler mit einer benutzerdefinierten Nachricht einschließen]
das würde von tryhard nicht gemeldet werden.

Das ist der Punkt – der try Vorschlag vereinfacht nur if err != nil return err nackte Rückgaben, er unterstützt keine Wrapping-Fehler mit einer benutzerdefinierten Nachricht und einem benutzerdefinierten Kontext.

Die einzige Wiederholung von if err != nil meiner Meinung nach behoben werden, indem man auch die Nullwerte der anderen Rückgabewerte angeben muss. Die Sprache könnte aktualisiert werden, um dies zu beseitigen. Zum Beispiel:

Im heutigen Go, wenn ich eine Funktion mit dieser Signatur habe:

func add(x, y string) (int, error)

Irgendwo in der Funktion müsste ich schreiben:

func add(x, y string) (int, error) {
    // ...
    if err != nil {
        return 0, err
    }

Den Writer zwingen, während der gesamten Funktion dieselben Nullwerte zu wiederholen.

Es wäre viel einfacher (und mit geringen Kosten für Fehlerausführlichkeit und Lesbarkeit), wenn die Sprache die Nullwerte für die fehlerfreien Rückgabewerte automatisch ausfüllen könnte:

func add(x, y string) (int, error) {
    // ...
    if err != nil {
        return ..., err
    }
    // ...
}
func main() {
    add("8", "beep") // returns 0, error(`strconv.ParseInt: parsing "beep": invalid syntax`)
}

Aus Erfahrung mit viel Code, der mit DB-Abfragen und -Aufrufen interagiert, kann ich sagen, dass es das einzige Negative an der Fehlerbehandlung im Go-Stil ist, die Nullwerte über die Funktionen hinweg wiederholen zu müssen. Ansonsten stimme ich dem Gedanken dieses Vorschlags zu: Lass if err != nil Ruhe!

Hinweis: Ja, benannte Rückgabewerte können dies _sort of_ erreichen (https://play.golang.org/p/MLV8Y52HUBY), aber nachdem ich einige Funktionen in meinen eigenen Codebasen mit dieser Technik implementiert hatte, wurde ich daran erinnert, wie viel von einem Fuß -gun benannte Rückgabewerte sind; Am Ende überschatte ich immer den benannten Rückgabewert.

Zum Beispiel, wenn Ihre Firmenversammlung zu
[Fehler mit einer benutzerdefinierten Nachricht einschließen]
das würde von tryhard nicht gemeldet werden.

Das ist der Punkt – der try Vorschlag vereinfacht nur if err != nil return err nackte Rückgaben, er unterstützt keine Wrapping-Fehler mit einer benutzerdefinierten Nachricht und einem benutzerdefinierten Kontext.

Das stimmt, ich dachte an die Variante, die das Hinzufügen einer beschreibenden Zeichenfolge ermöglicht. Die überwiegende Mehrheit (~4000 / 4500) unserer Fehlerrückgaben sind errgo.Mask(err) ohne Kontext, die meiner Meinung nach einer Beschreibung ohne Beschreibung try() Funktionalität, da errgo Stack-Informationen hinzufügt und try (noch) nicht.

@ianlancetaylor hier gibt es einen Vorschlag. @miekg schlägt vor, dass Sie als einer der Führer unserer Sprache nicht länger den Ersatz von if err != nil durch ein anderes Konstrukt verfolgen, das dem Geist der Fehlerbehandlung widerspricht, wie er von den ursprünglichen Go-Autoren beschlossen wurde. Für mich persönlich fühlt es sich so an, als würden Sie versuchen, die Unwichtigkeit dieser Anfrage zu behaupten, indem Sie sie in golang-nuts anstatt sie wie unsere anderen Vorschläge zu behandeln. Das ist vielleicht nicht Ihre Absicht, aber es ist die Wirkung, die ich spüre.

Unsere Methode der Fehlerbehandlung ist einzigartig und ich glaube, dass sie einen enormen Mehrwert gegenüber anderen Sprachen darstellt. Es hat meine Einstellung zu Fehlern in den von mir erstellten Systemen völlig verändert und als Ergebnis wurde ich ein stärkerer Software-Ingenieur. Ich möchte nicht, dass wir uns an die laute Minderheit oder Außenstehende wenden, um mehr Go-Entwickler zu bekommen. Ich denke, wir sollten bei bestimmten Dingen eine harte Linie verfolgen, wobei die Art und Weise, wie wir mit Fehlern umgehen, eine davon ist, weil sie uns letztendlich besser macht, indem wir die Kürze des Codes abwägen.

Dies ist eine Gelegenheit für das Team innerhalb von Google, mehr Vertrauen in die Community aufzubauen oder den Weg fortzusetzen, auf dem wir uns derzeit befinden und der nicht gut für die Sprache, das Ökosystem oder seine Benutzer ist.

Ich bitte das Go-Team, diesen Vorschlag unverändert anzunehmen, während es weiterhin andere, nicht verwandte Sprachiterationen verfolgt, die einen deutlicheren Mehrwert bieten.

Der Tracker hat vielleicht kein Threading, aber ich persönlich hätte viel lieber die Garantie, dass dieser Vorschlag in offizieller Funktion beantwortet wird und nicht in den Google-Konzern verbannt wird, wo er leise in Vergessenheit geraten kann.

Auch in der Google-Gruppe wurde das Thema bereits diskutiert.

Die aktuelle Version von #32437 ist unbefriedigend. Das eingebaute try() verbirgt viele Ausführungspfade für das ungeübte Auge. Der ursprüngliche Vorschlag mit Check und Handle war sehr verständlich und das Schlüsselwort check stach hervor.

Nun sieht das eingebaute try() wie eine Funktion aus - es ist nicht offensichtlich, dass es den Kontrollfluss ändern kann. Wir haben auch panic(), aber es ist (glaube ich) immer in einer eigenen Zeile, hat einen prominenten Namen und wird selten verwendet. try() hingegen könnte sich in einem komplexen Ausdruck verstecken.

@theckman Robert hat die ersten Iterationen von Go mit Rob und Ken entworfen, und Robert und Russ sind dem Team früh beigetreten. Sie haben von Anfang an an Go gearbeitet. Ich denke, wir können ihnen vertrauen, dass sie wissen, ob ein Vorschlag "dem Geist der Fehlerbehandlung widerspricht, wie er von den ursprünglichen Go-Autoren beschlossen wurde".

Ich mag das Prinzip eines Vorschlags, der die Fehlerbehandlung so einfrieren würde, wie es heute ist, nicht. Ein solcher Vorschlag würde alle zukünftigen Vorschläge zu diesem Thema verbieten.

Warum nicht einfach akzeptieren, das Design stattdessen zu iterieren? Wir hatten den Check/Handle-Vorschlag. Aber einige Nachteile wurden diskutiert. Dies führte zu dem Versuchsvorschlag. Einige Nachteile dieses Vorschlags werden nun diskutiert. Vielleicht führt dies zu einem anderen, besseren Vorschlag, bis der richtige Ansatz gefunden ist.

Unsere Methode der Fehlerbehandlung ist einzigartig

Die Fehlerbehandlung in Rust ist konzeptionell ähnlich wie in Go (Fehler sind Werte, expliziter Kontrollfluss, außer dass wir mehrere Rückgabewerte verwenden, wenn sie stattdessen Summentypen verwenden). Rust hatte das gleiche Problem wie Go mit ausführlicher Fehlerbehandlung. Dies veranlasste Rust, den Versuch hinzuzufügen! Makro und schließlich das ? Operator. Ich würde sagen, dass die Rust-Community in Bezug auf die Fehlerbehandlung noch strenger ist als die Go-Community (die RFCs und Diskussionen zur Fehlerbehandlung sind aufschlussreich). Sie haben einen Weg gefunden, die Ausführlichkeit der Fehlerbehandlung zu verringern, ohne die rutschige Steigung einer schlechten Fehlerbehandlung. Ich bin sicher, wir können das auch.

die Entwicklung, auf der wir uns derzeit befinden, ist nicht gut für die Sprache, das Ökosystem oder ihre Benutzer

Worüber redest du? Go wird ständig verbessert. Es ist erstaunlich, kostenlos Zugang zu einer so großartigen Sprache, Tools und Dokumentation zu haben (wie bei der freien Meinungsäußerung).

@theckman Robert hat die ersten Iterationen von Go mit Rob und Ken entworfen, und Robert und Russ sind dem Team früh beigetreten. Sie haben von Anfang an an Go gearbeitet. Ich denke, wir können ihnen vertrauen, dass sie wissen, ob ein Vorschlag "dem Geist der Fehlerbehandlung widerspricht, wie er von den ursprünglichen Go-Autoren beschlossen wurde".

Ich mag das Prinzip eines Vorschlags, der die Fehlerbehandlung so einfrieren würde, wie es heute ist, nicht. Ein solcher Vorschlag würde alle zukünftigen Vorschläge zu diesem Thema verbieten.

Warum nicht einfach akzeptieren, das Design stattdessen zu iterieren? Wir hatten den Check/Handle-Vorschlag. Aber einige Nachteile wurden diskutiert. Dies führte zu dem Versuchsvorschlag. Einige Nachteile dieses Vorschlags werden nun diskutiert. Vielleicht führt dies zu einem anderen, besseren Vorschlag, bis der richtige Ansatz gefunden ist.

Unsere Methode der Fehlerbehandlung ist einzigartig

Die Fehlerbehandlung in Rust ist konzeptionell ähnlich wie in Go (Fehler sind Werte, expliziter Kontrollfluss, außer dass wir mehrere Rückgabewerte verwenden, wenn sie stattdessen Summentypen verwenden). Rust hatte das gleiche Problem wie Go mit ausführlicher Fehlerbehandlung. Dies veranlasste Rust, den Versuch hinzuzufügen! Makro und schließlich das ? Operator. Ich würde sagen, dass die Rust-Community in Bezug auf die Fehlerbehandlung noch strenger ist als die Go-Community (die RFCs und Diskussionen zur Fehlerbehandlung sind aufschlussreich). Sie haben einen Weg gefunden, die Ausführlichkeit der Fehlerbehandlung zu verringern, ohne die rutschige Steigung einer schlechten Fehlerbehandlung. Ich bin sicher, wir können das auch.

die Entwicklung, auf der wir uns derzeit befinden, ist nicht gut für die Sprache, das Ökosystem oder ihre Benutzer

Worüber redest du? Go wird ständig verbessert. Es ist erstaunlich, kostenlos Zugang zu einer so großartigen Sprache, Tools und Dokumentation zu haben (wie bei der freien Meinungsäußerung).

Die Entwicklungsgeschichte von Rust zeigt, dass die Typen dahinter keine Ahnung hatten, was sie tun. Sie haben im Grunde genommen Fehlerverarbeitungsprinzipien von Haskell kopiert, aber diese passen nicht gut zur zwingenden (realen?) Programmierung. Ihr ? Makro ist nur ein Workaround für das anfänglich fehlgeschlagene Fehlerverarbeitungssystem.

@ianlancetaylor hier gibt es einen Vorschlag. @miekg schlägt vor, dass Sie als einer der Führer unserer Sprache nicht länger den Ersatz von if err != nil durch ein anderes Konstrukt verfolgen, das dem Geist der Fehlerbehandlung widerspricht, wie er von den ursprünglichen Go-Autoren beschlossen wurde. Für mich persönlich fühlt es sich so an, als würden Sie versuchen, die Unwichtigkeit dieser Anfrage zu behaupten, indem Sie sie in golang-nuts anstatt sie wie unsere anderen Vorschläge zu behandeln. Das ist vielleicht nicht Ihre Absicht, aber es ist die Wirkung, die ich spüre.

Unsere Methode der Fehlerbehandlung ist einzigartig und ich glaube, dass sie einen enormen Mehrwert gegenüber anderen Sprachen darstellt. Es hat meine Einstellung zu Fehlern in den von mir erstellten Systemen völlig verändert und als Ergebnis wurde ich ein stärkerer Software-Ingenieur. Ich möchte nicht, dass wir uns an die laute Minderheit oder Außenstehende wenden, um mehr Go-Entwickler zu bekommen. Ich denke, wir sollten bei bestimmten Dingen eine harte Linie verfolgen, wobei die Art und Weise, wie wir mit Fehlern umgehen, eine davon ist, weil sie uns letztendlich besser macht, indem wir die Kürze des Codes abwägen.

Dies ist eine Gelegenheit für das Team innerhalb von Google, mehr Vertrauen in die Community aufzubauen oder den Weg fortzusetzen, auf dem wir uns derzeit befinden und der nicht gut für die Sprache, das Ökosystem oder seine Benutzer ist.

Ich bitte das Go-Team, diesen Vorschlag unverändert anzunehmen, während es weiterhin andere, nicht verwandte Sprachiterationen verfolgt, die einen deutlicheren Mehrwert bieten.

Sie können mit dem aktuellen Schriftsystem aus den 60er Jahren nichts Ernstes machen. Sie müssen endlich 80er-Jahre-Ideen in ihrem Go 2.0 ausleihen

Worüber redest du? Go wird ständig verbessert. Es ist erstaunlich, kostenlos Zugang zu einer so großartigen Sprache, Tools und Dokumentation zu haben (wie bei der freien Meinungsäußerung).

@ngrilly , der letzte Teil ist wahrscheinlich für eine größere Diskussion. Ohne diesen Vorschlag zu entgleisen, aber diesen Kommentar etwas zu schließen, wächst das Gefühl der Diskrepanz zwischen Benutzern und Führung in der Gemeinschaft/im Ökosystem.

Für den Rest der Diskussion denke ich nicht, dass es ein Gewinn ist, der Syntax mehr kognitiven Overhead hinzuzufügen. Ich bin froh, dass sie etwas gefunden haben, das für sie funktioniert, wir sind nicht sie.

Ich habe gerade einen Vorschlag für die Inline-IF-Anweisung geöffnet: https://github.com/golang/go/issues/32860

Referenz: https://github.com/golang/go/issues/32825#issuecomment -506707539

Wie viel schöner würde diese Welt werden, wenn jeder, der seinen Vorschlag zu einer neuen Golang 2.0-Funktion einreicht, die er so gerne hätte, auch einen Zweig seines Forks von https://github.com/golang/go (und was auch immer) bereitstellen würde Repositorien erforderlich), die diesen Vorschlag umsetzt.

Sind Sie nicht einverstanden?

@av86743 Scheint den Rahmen dieses Vorschlags zu

Ich sehe darin eine Herausforderung, wie zum Beispiel das Risiko einer Menge vergeudeter Mühen, bevor jemand es aufgrund von etwas im Vorschlagsdokument selbst ablehnt. Dann haben Sie die ganze Zeit auf einer Gabel verbracht, die nicht einmal überprüft wird.

wie wäre es mit dieser Syntax:

# call error handler
try callFunction(), errorHandler()

# error handler with anonymous function
variable := try callSomething(), func(err *Error) { # error handling }

@theckman Ich entschuldige mich, wenn mein Vorschlag, diese Diskussion an einen anderen Ort zu verschieben, den Anschein

Da Sie "die ursprünglichen Go-Autoren" erwähnen, ist es meiner Meinung nach erwähnenswert, dass der "Versuchsvorschlag" von @griesemer gemacht wurde, der einer der drei ursprünglichen Go-Autoren ist.

Ich stimme diesem Vorschlag voll und ganz zu, ich denke, das einzige, was geändert werden muss, ist go fmt, make go fmt, um eine Zeile if-Anweisung zuzulassen.

Ich möchte wirklich eine Zeile von

if err != nil { return wrappedErr{err} }

statt drei Zeilen

if err != nil {
    return wrappedErr{err}
}

@av86743 Scheint den Rahmen dieses Vorschlags zu

@theckman Du

Ich sehe darin eine Herausforderung, wie zum Beispiel das Risiko einer Menge vergeudeter Mühen, bevor jemand es aufgrund von etwas im Vorschlagsdokument selbst ablehnt. Dann haben Sie die ganze Zeit auf einer Gabel verbracht, die nicht einmal überprüft wird.

Es wäre nur eine "vergeudete Anstrengung" für [... _Beschreibung in einer ganz angemessenen Sprache der Kürze halber weggelassen_ ...].

Für einen Programmierer wäre das eine triviale, aber nützliche Übung und gleichzeitig ein Dienst an der Go-Community.

@ av86743 Ich denke, was Sie vorgeschlagen haben, ist eine interessante Idee und ich wollte nicht, dass sie als Kommentar in einem nicht verwandten Problem verloren geht. Wenn Sie kein Interesse daran haben, dies offiziell zu prüfen, entschuldige ich mich dafür, dass Sie sich dafür einsetzen, dass Sie ein separates Thema ansprechen.

Auch wenn dieser konkrete Vorschlag von @griesemer stammt, finde ich es kaum zu glauben, dass er seit zehn Jahren vor innerer Wut über die Ausführlichkeit der unverpackten Fehlerrückgaben in Go brodelt. Er ist jedoch ein ausgezeichneter Ingenieur, und Ingenieure finden Lösungen für (wahrgenommene) Probleme; es ist sehr schwer, sie aufzuhalten. Wir _gefallen_ an der Lösung von Problemen. Das bloße Erschnuppern eines Problems reicht uns aus, um alle möglichen Ideen zu entwickeln. Wenn dieser Denkprozess erst einmal ziemlich weit fortgeschritten ist, fällt es jedem von uns schwer, unsere vermeintlichen Lösungen emotional loszulassen und zu bedenken, dass es _vielleicht doch kein wirkliches Problem ist_. Schließlich haben wir intellektuell ein Baby bekommen, und es ist nicht einfach, es einfach aufzugeben.

Für das, was es wert ist, ist mein privater Verdacht, dass der Argumentationsprozess des Go-Teams in dieser Hinsicht ungefähr so ​​abgelaufen ist:

  1. Go ist sehr beliebt und weit verbreitet, daher haben natürlich Tausende von Menschen Kommentare, Vorschläge und Beschwerden dazu.
  2. Sie können nur so lange mauern. Das Go-Team fühlt sich unter enormem Druck, _etwas_ gegen den Lärm der Community zu unternehmen.
  3. Das ist etwas.

wie wäre es mit einer catch()-Funktion in defer to catch, wenn beim try ein Fehler wie recovery() gefunden wurde.
Beispiel:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

auf viele Funktionen

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
    file1 := open("file1")
    defer file1.Close()
    file2 := open("file2")
    defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

wie wäre es mit einer catch()-Funktion in defer to catch, wenn beim try ein Fehler wie recovery() gefunden wurde.
Beispiel:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

auf viele Funktionen

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
  file1 := open("file1")
  defer file1.Close()
  file2 := open("file2")
  defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

Wie hilft dies, jeden Fehler separat zu behandeln?

Einige Klarstellungen:

  1. Der Vorschlag von try führt weder eine neue Syntax noch ein neues Schlüsselwort ein, im Gegensatz zu dem, was einige Leute in diesem Thread behaupten. Es führt lediglich eine neue integrierte Funktion ein, ungefähr die minimalste Änderung, die man vornehmen kann, um neue Funktionen hinzuzufügen. Bitte seien Sie präzise, ​​wenn Sie dies besprechen, denn es ist wichtig. Es gibt einen großen Unterschied zwischen dem Hinzufügen einer neuen Syntax und einem neuen Schlüsselwort und einem integrierten. Ersteres ist eine große Änderung, letzteres eine relativ geringfügige Ergänzung. Was der Vorschlag von try vorschlägt, ist eine relativ kleine Ergänzung.

  2. Ich stimme @ianlancetaylor zu, dass diese Diskussion woanders besser geführt wird (Golang-Nüsse). Hier liegt kein Vorschlag vor.

  3. In der Tat, @bitfield , ich habe null "innere Wut über die Ausführlichkeit von unverpackten Fehlerrückgaben in Go" , danke :-) Aber ich denke, dass die Fehlerprüfung ausführlicher ist als vielleicht notwendig; und die Tatsache, dass dieselbe Meinung von der Community wiederholt vorgebracht wurde, ist ein klarer Indikator dafür, dass wir (das Go-Team) mit dieser Überzeugung nicht allein sind. Ich würde nicht so weit gehen zu sagen, dass es viel Druck gibt, "etwas" zu tun - daran arbeiten wir schon seit langer Zeit hin und her, und wir sind zufrieden, auf den "richtigen" Ansatz zu warten .

Der Vorschlag von try ist ungefähr die minimalste Lösung, die wir gefunden haben (mit erheblicher Hilfe von Community-Beiträgen), die das Problem der Fehlerprüfung angeht. Der Vorschlag von try geht sehr deutlich darauf ein, dass es _überhaupt nicht hilft_, wenn jeder Fehlertest eine spezifische Behandlung des Fehlers erfordert. try hilft nur, wenn alle Fehler in einer Funktion getestet und gleich behandelt werden (und dann empfehlen wir defer ) oder wenn sie einfach zurückgegeben werden. Es ist schwer, hier expliziter zu sein, aber wiederholen wir, was der Vorschlag besagt : try wird nicht in allen Fehlerszenarien helfen. Es hilft in einer erheblichen Anzahl von Fällen. Verwenden Sie für alles andere if Anweisungen.

@griesemer try ist zu fehleranfällig: Es gibt kein RAII in Go, daher können wir die Funktion in vielen Fällen nicht einfach verlassen.

@sirkon , ich bin mir nicht sicher, inwiefern RAII für diese Diskussion relevant ist. try ersetzt vorhandene Muster von if ..., err := f(); err != nil { return ..., err } durch ein ... := try(f()) . Wenn es bei der Verwendung von try einen Fehler zur Ressourcenfreisetzung gab, dann existierte dieser sicherlich auch schon vorher. Die Einführung von try verbessert oder verhindert den Fehler bei der Ressourcenfreisetzung weder.

@sirkon , ich bin mir nicht sicher, inwiefern RAII für diese Diskussion relevant ist. try ersetzt vorhandene Muster von if ..., err := f(); err != nil { return ..., err } durch ein ... := try(f()) . Wenn es bei der Verwendung von try einen Fehler zur Ressourcenfreisetzung gab, dann existierte dieser sicherlich auch schon vorher. Die Einführung von try verbessert oder verhindert den Fehler bei der Ressourcenfreisetzung weder.

Lies den Thread, es gab ein Beispiel:

info := try(try(os.Open(fileName)).Stat())

@sirkon Ich habe dieses Beispiel jetzt mehrmals gesehen. Ich stimme zu, dass es interessant ist. Aber denken wir noch ein bisschen darüber nach. Das vorgeschlagene eingebaute try ist im Grunde syntaktischer Zucker für eine bestimmte Art von Boilerplate, die in Go-Code zu finden ist. So können wir dieses Beispiel in den Originalcode umwandeln.

    f, err := os.Open(fileName)
    if err != nil {
        return err
    }
    info, err := f.Stat()
    if err != nil {
        return err
    }

Dieser Code hat den gleichen Fehler. Ich habe sicherlich Code wie diesen gesehen. Es ist für mich nicht offensichtlich, dass das eingebaute try diesen Fehler leichter zu schreiben oder schwerer zu erkennen macht.

[Sieht aus, als hätte @ianlancetaylor mich gerade geschlagen.]

@sirkon Dieser Fehler ist bereits möglich, try oder nicht - Go hindert Sie nicht daran, schlechten Code zu schreiben. Oder umzukehren, schlechten Code als Grund zu verwenden, warum try nicht zugelassen werden sollte, ist kein überzeugendes Argument. Stattdessen sollte go vet problematische Fälle kennzeichnen.

defer ist das Go-Idiom zum Aufräumen, wenn eine Funktion zurückkehrt, und das funktioniert gut. Der richtige Ansatz wäre hier natürlich:

f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

vergleiche das mit:

f, err := os.Open(filename)
if err != nil {
   return ..., err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
   return ..., err
}

Mit try konzentriert sich die Quelle auf das Hauptanliegen: das Abrufen der Dateiinformationen einer Datei. Beim traditionellen Ansatz ist der Quellcode größtenteils besorgt über mögliche Fehler; und es ist alles gleich. Selbst wenn wir den Fehler dekorieren möchten, funktioniert der Ansatz von try wunderbar:

defer errd.Wrap(&err, "failed to do X for %s", filename)
f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

so etwas wie ein errd Paket verwenden (siehe Ausgabe #32676).

@griesemer
Mein zukünftiges Selbst, das Code-Review durchführt, schreit immer noch, dass der Kontrollflussmechanismus in einer eigenen Zeile sein sollte. Kann dieser Ansatz im aktuellen Vorschlag gültig sein (keine Durchsetzung)? Neben der Lesbarkeit wird das Refactoring zu einer detaillierteren Fehlerbehandlungslogik erleichtert.

defer errd.Wrap(&err, "failed to do X for %s", filename)
f, err:= os.Open(filename)
try(err) // check is so much a better term
defer f.Close()
info, err := f.Stat()
try(err)

Außerdem sieht dieser Ansatz für handle hier gut aus, aber werden mehrere Verschiebungen nicht zu einem Durcheinander? Oder wird es eine funktionsbezogene Synchronisierung geben. Einmal (ich habe keine Klarstellung in der Fehlerausgabe gesehen)? Wenn ja, würden anonyme Funktionen einen eigenen Geltungsbereich erhalten? Und Shadowing... äh - wer ist zuerst, was ist zweiter?

Das alles fühlt sich an, als würde es am Ende zwei "Modi" zum Schreiben von Go-Code geben. Sagen Sie, es ist nicht so.

@griesemer Obwohl Sie Recht haben, ist der Fehler auch heute möglich, aber ich bin fest davon

Wenn dieser Vorschlag angenommen wird, werden sie eine Möglichkeit haben, die if err Boilerplate zu vermeiden, wodurch sie Code in einer ihnen vertrauten Weise schreiben können. Außer, dass sie fast ein Jahrzehnt Go-Code in Stackoverflow-Antworten, Blog-Posts usw. haben werden, die geschrieben wurden, bevor Try erstellt wurde. Sie werden schnell lernen, die err- und if-Anweisung mit try einfach fallen zu lassen. Sie möchten eine Dateigröße, in der sie Code in try einfügen können, bis sie auf das gewünschte Feld zugreifen können, genau wie bei Stat() im Try-Vorschlag. Es ist ein Muster, an das sie gewöhnt sind, daher ist es nur natürlich, dass sie es beim Schreiben von Go anwenden. Angesichts der Tatsache, dass das am stärksten gezielte Go OS alles als Datei behandelt, kann man davon ausgehen, dass mehr Ressourcenlecks auftreten werden.

Was mich zu dem Punkt bringt, warum ich der Aussage "das kannst du heute schon tun" vehement widerspreche - weil du es einfach nicht kannst. Sicher - Sie können ein Datei-Handle verlieren. Aber Go gibt einem Programmierer nicht die Möglichkeit, den Bezeichner im Geltungsbereich zu überspringen und damit auch die Datei durchsickern zu lassen. Jeder übersprungene Bezeichner f ist ein durchgesickertes Datei-Handle. Die Verwendung der Funktion _erfordert_, dass bestimmte bekannte Redewendungen im Go-Ökosystem gebrochen werden. Somit erhöht die Einführung des Features in seiner heutigen Form nachweislich das Risiko von Ressourcenlecks in Go.

Das heißt, wie ich in https://github.com/golang/go/issues/32825#issuecomment -506882164 erwähnt habe, würde ich tatsächlich try wenn ein paar kleine Anpassungen vorgenommen würden. Ich denke, die Änderung wäre eine willkommene Ergänzung zur Sprache. Ich denke, try braucht nur, es nur für das RHS einer Zuweisung gültig zu machen und den Rückgabewert nicht adressierbar zu machen. Machen Sie die "guten" Beispiele für die Verwendung von try (in der Regel ein Try pro Zeile) zur "einzigen" Möglichkeit, try zu verwenden, dh:

info := try(try(os.Open(filename)).Stat()) // compiler error
f := try(os.Open(filename)) // valid
// we were forced to assign f, so we still have an identifier to Close (serve linters and users alike)
defer f.Close()
info := try(f.Stat())
a, b := try(strconv.Atoi("1")), try(strconv.Atoi("2")) // also valid 
a, b := try(strconv.Atoi("1"), strconv.Atoi("2")) // maybe?
a, b := try strconv.Atoi("1"), strconv.Atoi("2")

Ich denke, dies wird natürlich besser in die Sprache passen, alle aktuellen Vorteile von try beibehalten (außer sie zu verschachteln, wenn Sie dies für einen Vorteil halten) ohne die Nachteile. Ich glaube nicht, dass das Verschachteln von Try irgendjemandem einen Gefallen tut, es spart sehr wenig, bietet aber unbegrenzte Möglichkeiten für Missbrauch. Ich fühle mich heute nicht besonders böse, also ist dies das Beste, was ich tun kann:

total := try(try(os.Open(filename)).Stat()).Size() + try(strconv.Atoi(try(ioutil.ReadAll(os.Stdin))))

Aber _wir_ werden an das Schlimmste denken, wenn Sie uns lassen.

@daved Das Einfügen von try(err) in eine zweite Zeile wird mit dem vorhandenen try Vorschlag vollständig unterstützt: try möchte einfach ein Argument, das zu einem oder mehreren Werten ausgewertet wird, bei denen der letzte Wert von ist Geben Sie error , was natürlich erfüllt ist, wenn Sie try(err) schreiben.

Ich bin nicht sicher, ob ich Ihren Bedenken bezüglich defer folgen kann - wenn verschiedene Handler erforderlich sind, ist defer möglicherweise nicht die richtige Wahl; stattdessen kann das traditionelle if erforderlich sein (wie im Angebot angegeben).

@cstockton Ich stimme zu, dass verschachtelte try 's sehr problematisch sein können; aber ich glaube auch, wenn wir try , würde der größte Teil des Codes wie die (gültigen) Beispiele aussehen, die

Aus Stilgründen haben wir keine Einschränkungen wie die, die Sie bevorzugen, in die Sprache aufgenommen - wir haben dafür go vet . Am Ende ist der Effekt für die geschriebene Software der gleiche. Aber indem wir es nicht in der Sprache haben, binden wir uns nicht fest. Es ist schwierig, diese Einschränkungen richtig zu machen, und sie machen die Spezifikation unnötig kompliziert. Es ist einfach viel einfacher, go vet anzupassen und es intelligenter zu machen, wenn wir mehr lernen, als die Sprache anzupassen.

@griesemer Danke für die Klarstellung. Wenn im Codebeispiel die erste Zeile var err error wäre, würde sich der Zeilenumbruch möglicherweise auf beide geprüften Fehler auswirken? Ich habe gesehen, dass die Rede davon ist, dass Shadowing ein Problem ist, das in Zukunft behandelt werden könnte. Was hat/könnt das damit zu tun haben?

wie wäre es mit einer catch()-Funktion in defer to catch, wenn beim try ein Fehler wie recovery() gefunden wurde.
Beispiel:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

auf viele Funktionen

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
    file1 := open("file1")
    defer file1.Close()
    file2 := open("file2")
    defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

Wie hilft dies, jeden Fehler separat zu behandeln?

wie andere Benutzer verpflichtet

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    file1 :=try open("file1")
    defer file1.Close()
    file2 :=try open("file2")
    defer file2.Close()

        //without try
       file3,err := open("file3")
       defer file3.Close()
 }

@daved In diesen Beispielen wurde angenommen, dass err der Name des Ergebnisses error . try wird immer diese Ergebnisfehlervariable setzen, unabhängig vom Namen (oder dem Fehlen eines Namens). Wenn Sie eine lokale Variable namens err , ist dies eine andere Variable. Wenn Sie auf den Ergebnisfehler verweisen möchten, muss dieser einen anderen Namen haben. Beachten Sie, dass dies bereits der Fall ist, dass dies nicht gültig ist:

func f(...) (..., err error) {
   var err error // << err already declared
   ...

Auf der anderen Seite, wenn Sie schreiben:

func f(...) (..., err error) {
   a, b, ... err := g() // redeclaration of err
   ...

das err in der Zuweisung ist einfach das gleiche, das in der Ergebnisparameterliste genannt wird. Hier ist nichts anders als das, was schon sehr lange der Fall war.

PS: Wir sollten wahrscheinlich aufhören, dieses Thema für try Diskussionen zu kapern, und zum ursprünglichen Vorschlag zurückkehren - er wird morgen (1. Juli) freigeschaltet und zur Diskussion gestellt.

@godcong Eine catch() Funktion (oder ähnlich) ermöglicht es Ihnen nur, den Fehler zu erhalten, nicht ihn zu setzen (und normalerweise möchte man den Fehler der einschließenden Funktion in einer verzögerten Funktion setzen, die als Fehlerhandler arbeitet). . Es könnte zum Laufen gebracht werden, indem catch() *error was die Adresse des Fehlerrückgabewerts der einschließenden Funktion ist. Aber warum nicht einfach den Fehlerergebnisnamen verwenden, anstatt der Sprache einen neuen Mechanismus hinzuzufügen? Siehe auch den try Vorschlag, in dem dies diskutiert wird.

Siehe auch PS oben .

@griesemer

Es ist schwer, hier expliziter zu sein, aber wiederholen wir, was der Vorschlag besagt: Try wird nicht in allen Fehlerszenarien helfen. Es hilft in einer erheblichen Anzahl von Fällen. Für alles andere verwenden Sie if-Anweisungen.

Ich denke, dies ist genau der fatale Fehler des try() Vorschlags: Wo es früher eine und nur eine Möglichkeit gab, Fehler zu überprüfen, werden es jetzt zwei sein, die sich durch die gesamte Codebasis vermischen. Außerdem können, zumindest für die Codebasis, an der ich arbeite, weniger als 20% von if err != nil durch try() , was zwar nicht unbedeutend ist, aber nicht lohnenswert genug erscheint, um ein in Fehlerbehandlungsstile aufgeteilt.

Persönlich hätte ich mir stattdessen ein Fehlerbehandlungskonstrukt gewünscht, das mächtig genug ist, um 95% aller if err != nil Fälle zu ersetzen. Ich denke, das hätten sich auch viele gewünscht.

@griesemer Ich stimme zu, dass die Leute lernen und Werkzeuge ein Muss sein werden, da die

Ich werde hier den Blickwinkel wechseln. Was ist der Anwendungsfall für die Verschachtelung von try-Anweisungen, die stark genug für die potenziellen Nebenwirkungen ist, die ich skizziert habe? Wie profitiert Go-Code heute davon, dass eine verkettebare, verschachtelbare, durch Try-Separation getrennte Klammer-Party überall wild auftauchen kann? Ich vermute, dass dies nicht der Fall ist und ich glaube nicht, dass jemand nach einer Verschachtelung gefragt hat, es kam mit dem Vorschlag, weil es als Funktion implementiert ist. Sie möchten keine Einschränkungen wie das Entfernen von Verschachtelungen / Adressierbarkeit hinzufügen, um Verschachtelungsmissbrauch oder subtile Fehler zu begrenzen, da dies die Sprachspezifikation komplexer machen würde. Ist das Thema hier, um zu verhindern, dass die Sprache kompliziert wird, oder um eine bessere Möglichkeit zum Umgang mit Fehlern hinzuzufügen?

Denn wenn es hier darum geht, die Sprachspezifikation nicht komplexer zu machen, ist die Wahl klar: Keine neue Funktion mit generischen Rückgaben & Parametern hinzufügen, ist beliebig verschachtelbar, bietet Kontrollfluss und ändert die Arität der übergebenen Werte ( aber nur, wenn sie eine bestimmte eingebaute Schnittstelle erfüllen) und wahrscheinlich mehr, dass ich zB eine Funktion mit beispielloser Komplexität vergesse. Wenn das Ziel darin besteht, die Fehlerbehandlung zu verbessern, sollte dies meiner Meinung nach getan werden, ohne neue Wege zur Fehlererzeugung einzuführen.

@sirkon Ich habe dieses Beispiel jetzt mehrmals gesehen. Ich stimme zu, dass es interessant ist. Aber denken wir noch ein bisschen darüber nach. Das vorgeschlagene eingebaute try ist im Grunde syntaktischer Zucker für eine bestimmte Art von Boilerplate, die in Go-Code zu finden ist. So können wir dieses Beispiel in den Originalcode umwandeln.

    f, err := os.Open(fileName)
    if err != nil {
        return err
    }
    info, err := f.Stat()
    if err != nil {
        return err
    }

Dieser Code hat den gleichen Fehler. Ich habe sicherlich Code wie diesen gesehen. Es ist für mich nicht offensichtlich, dass das eingebaute try diesen Fehler leichter zu schreiben oder schwerer zu erkennen macht.

Es ist für mich offensichtlich, dass ein "langsamerer" traditioneller Flow mehr Raum lässt, um zu bemerken, dass die Datei geschlossen werden sollte, und dies try provoziert diese Art von Lecks, da die Leute dazu neigen, Abkürzungen gegenüber langen Wegen zu bevorzugen.

@godcong Eine catch() Funktion (oder ähnlich) ermöglicht es Ihnen nur, den Fehler zu erhalten, nicht ihn zu setzen (und normalerweise möchte man den Fehler der einschließenden Funktion in einer verzögerten Funktion setzen, die als Fehlerhandler arbeitet). . Es könnte zum Laufen gebracht werden, indem catch() *error was die Adresse des Fehlerrückgabewerts der einschließenden Funktion ist. Aber warum nicht einfach den Fehlerergebnisnamen verwenden, anstatt der Sprache einen neuen Mechanismus hinzuzufügen? Siehe auch den try Vorschlag, in dem dies diskutiert wird.

Siehe auch PS oben .

Gos Schriftsystem ist in den 60er Jahren stecken geblieben und kann damit natürlich nicht gut mit Randfällen umgehen. Wenn Sie weitsichtig genug wären, um Ideen aus den 80er Jahren auszuleihen, hätten Sie Methoden, um subtile fehleranfällige Abläufe zu kontrollieren. Sie versuchen jetzt, ein Glas- und Metallgebäude in einem mittelalterlichen Dorf zu bauen: das Schlimme daran, dass diese mittelalterlichen Dörfer keinen Strom und keine Wasserleitungen haben, also werden Sie es auch nicht haben.

Es wird interessant sein zu sehen, inwieweit die neuen und verbesserten Fehlereinrichtungen in golang/go selbst eingesetzt werden. Wenn überhaupt.

Es wird auch interessant sein zu sehen, ob go2 fmt eine Option zur einfachen Ausgabe von go1.x .

Aus meiner eigenen Erfahrung habe ich Kontext in meinem zurückgegebenen Fehler hinzugefügt, indem ich:

import "github.com/pkg/errors"
func caller(arg string) error {
  val, err := callee(arg)
  if err != nil {
    return errors.Warpf(err, "failed to do something with %s", arg)
  }

  err = anotherCallee(val)
  if err != nil {
    return errors.Warpf(err, "failed to do something with %s", val)
  }

  return nil
}

Das Support-Team benötigt selten meinen Input für Probleme in der Produktion.

IMHO, ich glaube, dass es bei der Verbesserung der Fehlerbehandlung nicht darum geht, Boilerplate-Code zu reduzieren, sondern eine bequemere Möglichkeit zu bieten, Fehlern Kontext hinzuzufügen. Ich kann immer noch keinen vernünftigen Weg finden, try() zu verwenden.

Fügen Sie möglicherweise Kontext hinzu:

func caller(arg string) (err error) {
  defer func() {
    switch t := err.(type) {
      case CalleeErr:
        err = errors.Wrapf(err, "failed to do something with %s", arg)
      case AnotherCalleeErr:
        err = errors.Wrapf(err, "failed to do something with %s", val)
    }
  }()

  val := try(callee(arg))
  try(anotherCallee(val)
  return nil
}

Scheint nicht viel Tipparbeit zu sparen, aber wir opfern Lesbarkeit und Leistung.

Könnte am Ende try() auf diese Weise verwenden:

func caller(arg string) error {
    val, err := callee(arg)
    try(errors.Warpf(err, "failed to do something with %s", arg))

    err = anotherCallee(val)
    try(errors.Warpf(err, "failed to do something with %s", val))

    return nil
  }

Es reduziert ein paar Zeilen, das war's.

Für mich scheinen die meisten Lösungen für dieses Problem die eine Sache zu durchbrechen, von der ich dachte, dass sie von anderen Sprachen getrennt wird, die Ausnahmen verwenden: Der Fehlerbehandlungsmechanismus wird nicht als Kontrollfluss verwendet. Die meisten dieser Lösungen fügen eine Form von Kontrollfluss hinzu (check/handle, try, catch, Expect). An diesem Punkt denke ich, dass das Go-Team genauso gut Ausnahmen hinzufügen und damit Schluss machen könnte.

Obwohl ich es lieben würde, wenn wir einen Sonderfall von if und return könnten, der Rubin ähnelt

return err if err != nil

PS Entschuldigen Sie meine Unerfahrenheit im Sprachdesign, wenn ich etwas Dummes gesagt habe, zögern Sie bitte nicht, darauf hinzuweisen und mich aufzuklären.

wie wäre es mit einer catch()-Funktion in defer to catch, wenn beim try ein Fehler wie recovery() gefunden wurde.
Beispiel:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

auf viele Funktionen

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
  file1 := open("file1")
  defer file1.Close()
  file2 := open("file2")
  defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

Wie hilft dies, jeden Fehler separat zu behandeln?

wie andere Benutzer verpflichtet

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

  file1 :=try open("file1")
  defer file1.Close()
  file2 :=try open("file2")
  defer file2.Close()

        //without try
       file3,err := open("file3")
       defer file3.Close()
 }

In Ihrem Beispiel behandeln Sie alle Fehler mit dem gleichen Deffer. Was passiert, wenn Sie dem Fehler eine benutzerdefinierte Nachricht und benutzerdefinierte Informationen hinzufügen möchten?

@ianlancetaylor Hat jemand vorgeschlagen, den Operator ":=" zu erweitern, um "abgeleitete Rückgaben" zu unterstützen - Verhalten Sie sich im Grunde genau so, wie Sie es ohne Funktionsaufruf versuchen. Dies würde viele Bedenken lösen, die ich auf beiden Seiten gesehen habe:

  • try als Name für das Feature war umstritten, solange wir dies als Funktion implementieren, bleiben wir hängen, ihm einen Namen zu geben, bei dem ich nicht sicher bin, ob sich jemand zu 100% wohl fühlen wird.
  • try macht eine beispiellose Anzahl von Dingen, es hat Eingaben wie append() und beeinflusst den Kontrollfluss wie panic() während es den Platz eines allgegenwärtigen Musters einnimmt ( if err != nil ) .
  • try Verschachtelung ist eine Folge der Entscheidung, sie als Funktion zu implementieren, und nicht als Mehrwert, der aus einer strengen technischen Debatte abgeleitet wird.
  • try ist als Funktion implementiert, um die Abwärtskompatibilität aufrechtzuerhalten

Ich denke, wenn wir die Renditen einfach so ableiten würden, wie wir es tun, sehen die Dinge prägnant aus und fühlen sich mehr "Go" an, wie:

f, err := os.Open(filename)
if err != nil {
   return ..., err
}
defer f.Close()

info, err := f.Stat()
if err != nil {
   return ..., err
}

_Hier die korrekte Informatikbeschreibung für dieses Verhalten einfügen_, bis dahin mit abgeleiteten Rückgaben über kurze Variablendeklarationen zufrieden sein:

f := os.Open(filename)
defer f.Close()
info := f.Stat()

Was viel aufgeräumter aussieht als:

f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

Dies löst alle oben aufgeführten Bedenken, während es (meiner Meinung nach) ein bisschen mehr "Go like" ist und die Abwärtskompatibilität beibehalten wird. Es scheint auch etwas einfacher zu erklären, die "implizite" Rückgabe des Funktionsaufrufs für try() fühlt sich angesichts der allgegenwärtigen Bedeutung von try in jeder anderen Sprache wirklich fehl am Platz an. Ich kann nicht 100% für die Implementierung sprechen, aber es scheint, als ob es möglicherweise mit ungefähr dem gleichen Aufwand möglich wäre? Dem AssignStmt könnte ein Feld hinzugefügt werden, das angibt, welche Ausdrücke auf dem LHS ihre Fehlerwerte weggelassen haben, um dieselbe Backend-Kompilierung wie ein eingebautes zu informieren?

Ich mag die Fehlerprüfung so wie sie ist, aber wenn dies wirklich ein Problem ist, das gelöst werden muss, denke ich, dass ein neues Schlüsselwort wahrscheinlich die bessere Lösung ist. Jede Lösung wird wahrscheinlich Änderungen des Kontrollflusses beinhalten, und es ist am besten, dies so offensichtlich wie möglich zu machen.

In diesem Beispiel wird die Bedingung on jedes Mal ausgewertet, wenn err gesetzt wird.

func example() (foo int, err error) {
    on err != nil {
        return foo, err
    }

    foo, err = calcThis()
    foo, err = calcThat(foo)

    return
}

Dies funktioniert auch, ohne Namen für Rückgabewerte in der Funktionssignatur zu deklarieren.

func example() (*int, error) {
    var err error

    on err != nil {
        return nil, err
    }

    foo, err = calcThis()
    foo, err = calcThat(&foo)

    return &foo, nil
}

Dies kann auch mehrfach eingestellt werden. Hier ist ein erfundenes Beispiel:

func example() (*int, error) {
    var err error

    on err != nil {
        return nil, err
    }

    foo, err = calcThis()

    on err != nil {
        return &foo, err
    }

    foo, err = calcThat(&foo)

    return
}

In meinen Augen behält dies den Stil und die Lesbarkeit von Go bei, während gleichzeitig deutlich wird, was bei jedem Schritt passiert (wenn Sie das Paradigma erst einmal verstanden haben), da diese Bedingung effektiv nach jedem Vorkommen von err eingefügt wird.

Sie könnten sogar Folgendes tun:

func example() (foo int, err error) {
    var message string

    on err != nil {
        return foo, errors.Wrap(err, message)
    }

    message = "failed to calc this"
    foo, err = calcThis()

    message = "failed to calc that"
    foo, err = calcThat(foo)

    return
}

Schließlich hat dies eine Anwendbarkeit über die Fehlerbehandlung hinaus. Möchten Sie zurückkehren, wenn foo == 0? Noch ein erfundenes Beispiel:

func example(i int) bool {
    on x == 0 {
        return false
    }

    x = calcSomeInt(i)
    return true
}

@cstockton

Ich wollte gerade protestieren, dass es ein bisschen zu einfach wäre, Fehler auf diese Weise zu beseitigen, aber:

  • Dies ist bereits ein "Gotcha" mit Funktionen, die _nur_ Fehler zurückgeben, wie z. B. json.Unmarshal . Gute IME-Code-Reviewer lernen ziemlich schnell, darauf zu achten.
  • Es wäre immer noch ein Syntaxfehler in Funktionen, die keine Fehlerwerte zurückgeben, wie z. B. http.Handler Methoden.
  • Wenn Sie wüssten, wie man mit dem Fehler umgeht, würden Sie darüber nachdenken, ihn antizipieren und den Fehlerwert wahrscheinlich trotzdem erfassen, _damit_ Sie damit umgehen können.
  • Wenn Ihre Absicht darin bestand, alle aufgetretenen Fehler einfach zurückzugeben, um das Problem eines anderen (des Anrufers) zu machen, wird dies erreicht, mit dem kleinen Nachteil, dass Sie eine Gelegenheit verpassen, explizit mehr Anmerkungen für den Kontext hinzuzufügen.

Also, nachdem ich alle Vorteile abgewogen habe, die mir einfallen, und keine offensichtlichen Nachteile bemerkt habe, bin ich ... auf dem Zaun. Ich vermute, es gibt nicht offensichtliche Nachteile, die ich nicht bedacht habe. Aber ich beginne zu mögen, wohin dies führt, für jeden noch so kleinen Wert.

Ich hoffe, dies ist der richtige Weg, um zu antworten, und ich verpflichte mich nicht
schwerer Fauxpas.

Am 01.07.19 schrieb Chris Stockton [email protected] :

@ianlancetaylor Hat jemand vorgeschlagen, den ":="-Operator zu erweitern, um zu unterstützen
"inferred return" - Verhalten Sie sich im Grunde genau so, wie Sie es ohne Funktion versuchen
Anruf. [ ... ]

Was ich an diesem Fall faszinierend finde, ist, dass wir etwas haben
analog zum Paradigma "Komma OK", wo jetzt das "Err" weggelassen wird
Abtretungsempfänger ist unter bestimmten genau definierten Umständen zulässig. Wert
anmerken, aber eindeutig nicht ausreichen, um dies gültig zu machen
Vorschlag.

Lucio.

Dieser Fehler ist bereits möglich, versuchen Sie es oder nicht - Go verhindert nicht, dass Sie schlechten Code schreiben. Oder umgekehrt, schlechten Code als Grund zu verwenden, warum Try nicht erlaubt sein sollte, ist kein überzeugendes Argument. Stattdessen sollte ein Tierarzt auf problematische Fälle hinweisen.

@griesemer Ich stimme nicht zu. Obwohl es Tastenanschläge einsparen kann, wurde die Zeigerarithmetik von Go ausgeschlossen, da es das Schreiben von schlechtem, fehlerhaftem Code erleichtert. Ich glaube, dass dies die gleiche Art von Funktion ist, die die Erkennung von Fehlern erschwert.

data := try(ioutil.ReadAll(try(os.Open("foo.txt"))))

Das typische Beispiel mit try verwendet Beispiele wie die obigen, die für mich offensichtlich einen Dateideskriptor durchsickern lassen. (Die Tatsache, dass Go solche Deskriptoren in der aktuellen Implementierung ohne Wissen der Benutzer in der Laufzeit aufspürt und schließt, mag uns tröstlich sein, aber es gibt sowieso ein besseres Beispiel).

data := try(ioutil.ReadAll(try(http.Get("http://example.com/")).Body))

Das obige liest sich wie korrekter Code, ignoriert jedoch die Anforderung, dass der Antworttext vollständig gelesen und anschließend auf dem glücklichen Pfad geschlossen wird. Wenn Sie es lange genug betrachten, sollte die ekelhafte Eleganz des falschen Beispiels deutlich machen, dass wir solche Fehler in Zukunft sehen werden.

Als jemand, der an dieser Stelle mehr überprüft als Code schreibt, würde ich es vorziehen, dass Go keine Funktionen hinzufügt, die Fehler so verlockend machen.

@jesse-amano Die Verwendung des Zuweisungsoperators verhindert tatsächlich, dass dieser Fall möglich ist, ohne eine explizite Zuweisungsanweisung verhalten sich die folgenden genau wie heute, dh:

json.Unmarshal(...)
(http.Handler)(h).ServeHTTP(w, r)

Werte, die nur einen Fehler zurückgeben, können wie return json.Unmarshal(...) und können auch in kompakterer Form dargestellt werden, da kein Wert außerhalb des if-Blocks vorhanden sein muss.

if err := json.Unmarshal(...); err != nIl {
    return err
} 

Was ich in diesem Fall faszinierend finde, ist, dass wir etwas Analoges zum "Komma-OK"-Paradigma haben, bei dem das Weglassen des "Err"-Bevollmächtigten jetzt unter bestimmten klar definierten Umständen zulässig ist. Erwähnenswert, aber eindeutig nicht ausreichend, um dies zu einem gültigen Vorschlag zu machen.

Das Verhalten wäre identisch, wenn Sie es ohne Klammern oder willkürliche Verschachtelung und Verkettung versuchen würden. Ich denke, es wird schwierig sein, etwas zu finden, von dem die Mehrheit der Leute denkt, dass es sich natürlich anfühlt, ohne die Sprache zu brechen. Ich gebe zu, da es den Anschein hat, dass die Go-Autoren ziemlich entschlossen sind, diese Art von Funktion zu Go hinzuzufügen, suche ich wirklich tief nach Alternativen, weil ich absolut überzeugt bin, dass try in seiner aktuellen Form nicht gut zu Go passt. Das ist das Beste, was ich mir vorstellen kann, um BC nicht zu brechen, aber vielleicht fühlt es sich für genug Leute immer noch nicht richtig an. So oder so hoffe ich, dass der Versuchsvorschlag abgelehnt wird oder jemand etwas findet, auf das sich viel mehr Leute einigen können.

edit: @jesse-amano Ich habe deinen Punkt komplett übersehen, sorry! Ich nehme an, in einer Funktion zu sein, die keinen Fehler zurückgibt, würde einen typischen Kompilierungsfehler für die Zuordnungsanzahlkonflikt anzeigen? Ich kann mir vorstellen, dass Try wahrscheinlich eine neue Art von Compiler-Fehlermeldung dafür einführen würde.

@beoran Zu https://github.com/golang/go/issues/32825#issuecomment -507126700 : Die Fehlerbehandlung ist bereits von Situation zu Situation unterschiedlich: Manchmal geben wir den Fehler unverändert zurück, manchmal geben wir einen dekorierten Fehler zurück, manchmal handeln wir auf einen Fehler, und manchmal können wir einen Fehler (richtig) ignorieren. Das einzige, was sie alle gemeinsam haben (außer wenn wir den Fehler ignorieren) ist der err != nil Test (den wir bereits auf mehr als eine Weise durchführen können). So schön es auch wäre, wenn ein neues Sprachfeature all diese Fälle (oder 95% davon) erfassen könnte, so ist es doch sehr wahrscheinlich, dass ein solches Konstrukt andere Kontrollkonstrukte, die wir bereits haben, auf nicht orthogonale Weise stört. Das heißt, das Beste, was wir hoffen können, ist, einige dieser Fälle (vielleicht 20 %, vielleicht 50 % davon) zu verbessern.

@cstockton Bezüglich https://github.com/golang/go/issues/32825#issuecomment -507136111: Wenn verschachtelte try 's die einzige verbleibende Straßensperre sind und go vet nicht gut genug ist , ich denke, wir können in Betracht ziehen, verschachtelte try 's zu verbieten - das wäre einfach genug. Aber im Moment denke ich, dass wir uns noch in der FUD-Phase (Angst, Unsicherheit und Zweifel) befinden und praktisch niemand ernsthaft mit try experimentiert hat. Ich stelle fest, dass die Leute, die dies getan haben, positiv berichtet haben.

PS: Das Thema try ist wieder offen für Feedback. Lass uns dort weitermachen.

@cstockton Oh, absolut. Um das klarzustellen, der Punkt, den ich anstrebte, war, dass es in den meisten Fällen _schon_ schlechte Praxis ist, Funktionen wie json.Unmarshal aufzurufen, ohne den Fehlerwert zu erfassen, aber normalerweise nicht als schlechte Vorgehensweise für defer file.Close() von defer func() { err = file.Close(); if err != nil { ... }; }() , und wir haben gelernt, den Unterschied ziemlich leicht zu erkennen. So wird es (wahrscheinlich) mit Ihrem Änderungsvorschlag sein. Ich zuckte anfangs bei dem Gedanken zusammen, dass jemand unschuldig foo := strconv.ParseFloat(bar, 64) benutzte, als er beabsichtigte, den Fehler zu beheben, aber nach kurzer Überlegung glaube ich nicht wirklich, dass es ein Problem ist.

@sirkon Zu https://github.com/golang/go/issues/32825#issuecomment -507167388 : Bitte lassen Sie solche eindeutig unqualifizierten Aussagen aus der Diskussion - sie haben hier nichts zu suchen. Nur um es festzuhalten, viele Ideen in Go stammen eher aus den 80ern (Pakete, separate Zusammenstellung, etc.) als aus den 60ern und viele sind viel jünger (Goroutinen, Interfaces). Diese Ideen mögen noch alt erscheinen, aber sie haben sich bewährt (zumindest die kurze Zeit, in der CS existiert).

@turtleDev Bezüglich https://github.com/golang/go/issues/32825#issuecomment -507231353: Go führt die Ausnahmebehandlung durch, und zwar mit panic und defer und recover - wir nennen es einfach nicht "Ausnahmebehandlung", weil dieser Begriff eine für Go irreführende Konnotation hat. Aber um es klarzustellen, Go kann alles, was raise mit try-catch tun kann und noch mehr (denn im Gegensatz zu try-catch können defer verwendet werden bedingt). Vielen Dank.

@sorenvonsarvort Bezüglich https://github.com/golang/go/issues/32825#issuecomment -507268293: Wenn Sie für jeden Fall eine andere Fehlerdekoration wünschen, verwenden Sie eine if Anweisung. Bitte beachten Sie das Design-Dokument. Diese Frage wurde mittlerweile schon oft beantwortet. Vielen Dank.

@cstockton Zu https://github.com/golang/go/issues/32825#issuecomment -507306652: Ja, wir haben uns über einen solchen Mechanismus Gedanken gemacht. Konkret haben wir uns überlegt, "den Spieß umzudrehen" und statt try einfach handle bereitzustellen, was einen Fehlerhandler deklariert. Wenn ein Handler vorhanden ist (und nur dann), würde man einfach das err im LHS einer Zuweisung weglassen und es würde implizit überprüft (wie bei einem unsichtbaren try ). Es sieht gut aus, ist aber auch völlig unsichtbar, was eine große rote Fahne ist. Wir möchten, dass die Ausnahmebehandlung an jeder Stelle im Code explizit sichtbar ist. Ohne dies wäre es fast unmöglich, Code zu lesen und zu sehen, wo die Fehlerprüfung stattfindet.

@griesemer danke für die

@matthew-noken Bezüglich https://github.com/golang/go/issues/32825#issuecomment -507323973: Sie schlagen eine interessante Idee vor; es sieht dem Watchpoint-Mechanismus eines Debuggers sehr ähnlich. Es gibt einige Fragen, die beantwortet werden müssten: Muss der on Block zurückgeben (ich vermute schon, weil man sonst ins Spaghetti-Code-Land gelangt)? Kann man mehr als eine solche on Anweisung haben? Wie kompliziert kann die Bedingung on sein (Sie muss bei jeder Aufgabe ausgewertet werden)? Beachten Sie, dass wir keine beliebigen Ausdrücke haben können, da wir die Variable mit der on Anweisung eindeutig identifizieren müssen. Außerdem etwas ein Gräuel in Go: Das Konstrukt on impliziert, dass unsichtbarer Code an anderer Stelle ausgeführt wird.

Wenn Sie dies genauer untersuchen möchten, schlage ich vor, dies an anderer Stelle zu besprechen (Golang-Nüsse oder ein anderer, neuer Vorschlag). Vielen Dank.

@as Bezüglich https://github.com/golang/go/issues/32825#issuecomment -507345821:

Pointer-Arithmetik wurde unter der Prämisse von Go ausgeschlossen, dass es einfach ist, schlechten, fehlerhaften Code zu schreiben

Eigentlich wurde es ausgeschlossen, weil es die Garbage Collection erschwert oder unmöglich gemacht hätte (und ja, man kann auch unsicheren Code schreiben). Aber der wichtigere Punkt hier ist, dass es konkrete Beweise und Erfahrungen gab, die diese Entscheidung unterstützten.

Es gibt noch keine Erfahrung und keinen Beweis dafür, dass verschachtelte try 's weit verbreitet oder verbreitet sein werden. Aber siehe https://github.com/golang/go/issues/32825#issuecomment -507358397.

@turtleDev Bezüglich https://github.com/golang/go/issues/32825#issuecomment -507368167 : Ein panic ist _exakt_ eine Ausnahme, und recover innerhalb einer zurückgestellten Funktion ist im Wesentlichen ein catch . Sie sind im Produktionscode möglicherweise schwieriger zu finden, da wir in Go davon abraten, Ihren Code mit Ausnahmen zu schreiben. sie sollten nur in Ausnahmefällen verwendet werden.

Zur Anzahl der Kontrollflussstrukturen: Der Vorschlag ist ganz klar, dass try einfach syntaktischer Zucker ist, sonst nichts.

Ich habe versucht, einige der jüngsten Kommentare in diesem Thread zu diesem Thread zu beantworten. Aber lassen Sie uns die neuen Kommentare zum try Vorschlag in der aktuellen try Ausgabe #32437 fortsetzen (ab heute wieder freigeschaltet); und lassen Sie dieses Thema für die leave err != nil alone Diskussion reserviert. Vielen Dank.

@cstockton Noch ein Kommentar zu https://github.com/golang/go/issues/32825#issuecomment -507306652 : Wenn wir das implementiert haben, dann beginnen wir mit

    func f() int { ... }
    ...
    x := f()

und wechseln zu

    func f() (int, error) { ... }

würde bedeuten, dass das Verhalten von x := f() plötzlich und stillschweigend ganz anders wäre.

Ich habe einige Experimente ähnlich denen von @lpar in allen unseren go-Repositorys durchgeführt. Die Ergebnisse sind in diesem Kern enthalten: https://gist.github.com/freeformz/55abbe5da61a28ab94dbb662bfc7f763

@ianlancetaylor Ich habe das Gefühl, dass dies in den meisten Fällen sehr gut funktionieren würde und die Einführung einer besseren Fehlerberichterstattung viel weniger wirkungsvoll wäre. Betrachten Sie vollständige Beispiele für die beiden Hauptfälle, zuerst gibt der Aufrufer einen Fehler zurück:

func f() int { ... }
func a() error {
    x := f() // if f() is updated to return an error, we get automatic error propagation by default
    ...
}

func b() {
    x := f() // if f() is updated to return an error, we get the same compiler error 
    // assignment mismatch: 1 variable but pkg.f returns 2 values
}

Ich denke, im allgemeinen Fall ist dies ein schöner Vorteil dieses Ansatzes, die Eckfälle, in denen dies ein Problem darstellt, sind meiner Meinung nach bereits brüchig. Ich kann mir nur vorstellen, dass es wirklich böse sein könnte, einen Mutex zu blockieren:

func (t *T) a() error {
   t.mu.Lock()
   x := f() // if f() is updated to return an error, we get automatic error propagation by default
   if x > 15 {
     t.mu.Unlock()
     return errors.New("t > 15")
   }
   ...
}

Aber ich denke, Code, der so geschrieben ist, ist bereits anfällig für Deadlocks, wenn er auf einen Aufruf einer fremden Bibliothek angewiesen ist, um erfolgreich einen gültigen Programmstatus zu haben. Wenn es in einem Bereich gespeichert ist, der über Panik hinaus leben kann, kann es zu einem Deadlock kommen, wenn dieselbe Bibliothek über NPE eine Laufzeitpanik einleitet. Darüber hinaus sind die historischen Kosten für das Leben auf dem Heap ein Hauptmotivator für das Schreiben von Code. Mit der Leistungsverbesserung von Single-Defers, die auf dem Stack leben, ist ein solcher Code nicht wirklich notwendig. Ich denke, dass jede Ableitung dieser Art von Fehler leicht behoben werden kann.

Schließlich können auch hier die Argumente zur Unterstützung des Mangels des "Ausprobierens" mit Werkzeugen angewendet werden. Angesichts der zunehmenden Akzeptanz von Go-Modulen haben wir eine schöne Gelegenheit, einen "Library-Upgrade-Linting" -Schritt einzuführen, um solche Änderungen den Benutzern deutlich vor Augen zu führen.

@griesemer

Zur Anzahl der Kontrollflussstrukturen: Der Vorschlag ist ganz klar, dass Try einfach syntaktischer Zucker ist, sonst nichts.

Entschuldigen Sie, aber try wird kein Makro (wie C) sein, also ist es für den Endbenutzer nur eine weitere Kontrolle der Flussanweisung.

Ich glaube, ich habe zu diesem Zeitpunkt keine objektiven Argumente, also gebe ich zu, dass wir vielleicht eine bessere Fehlerbehandlung brauchen, aber ich denke, dass try möglicherweise nicht die beste Lösung ist.

Auch dies sind meine Meinungen und ich vertrete sie nur. Ich bin mir sicher, dass sich das Go-Team viel mehr Gedanken darüber gemacht hat, als ich es jemals tun werde.

Seite: Es kommt mir seltsam vor, dass diese Ausgabe 1335 positive Stimmen hat, während der try Vorschlag (#32437) nur 279 negative Stimmen hat. Ich würde erwarten, dass Leute, die dies positiv bewerten, den try Vorschlag ablehnen, damit die Meinung der Community deutlicher wird, da sich diese beiden Vorschläge gegenseitig ausschließen.

@griesemer

Die Fehlerbehandlung ist bereits von Situation zu Situation unterschiedlich: Manchmal geben wir den Fehler unverändert zurück, manchmal geben wir einen dekorierten Fehler zurück, manchmal reagieren wir auf einen Fehler und manchmal können wir einen Fehler (korrekt) ignorieren.

Da stimmt, so viel ist offensichtlich.

Das einzige, was sie alle gemeinsam haben (außer wenn wir den Fehler ignorieren) ist der err != nil Test (den wir bereits auf mehr als eine Weise durchführen können). So schön es auch wäre, wenn ein neues Sprachfeature all diese Fälle (oder 95% davon) erfassen könnte, so ist es doch sehr wahrscheinlich, dass ein solches Konstrukt andere Kontrollkonstrukte, die wir bereits haben, auf nicht orthogonale Weise stört. Das heißt, das Beste, was wir hoffen können, ist, einige dieser Fälle (vielleicht 20 %, vielleicht 50 % davon) zu verbessern.

Die vorgeschlagene try() Anweisung "stört" auch if und return auf nicht orthogonale Weise, daher würde ich sagen, dass dieses Argument nicht richtig ist. Einige Leute hier mögen try() aus diesem Grund nicht, aber ich bin anderer Meinung. Go ist nicht Oberon, es ist einfach, aber nicht minimalistisch, Go ist praktischer.

Wir sind uns jedoch nicht einig, dass es sich sogar lohnt, sich mit einem Sprachkonstrukt zu beschäftigen, das, wie Sie selbst zugegeben haben, nur bedingt brauchbar und anwendbar ist und das mit if und return schon richtig gemacht werden kann. try() so überwältigt, dass sie es lieber gar nicht haben würden. Auch wenn es nicht orthogonal mit return ist, würden sich die meisten Go-Programmierer wahrscheinlich ein leistungsfähigeres, allgemein nützlicheres try() wünschen.

@beoran ,

Sie haben geschrieben, dass Sie sich ein "leistungsstärkeres", "allgemein nützlicheres" try() .

@griesemer erwähnte 4 Situationen:

  1. Fehler unverändert zurückgeben
  2. Einen dekorierten Fehler zurückgeben
  3. Auf einen Fehler reagieren
  4. Fehler ignorieren

try() löst 1 per Design: Es ist buchstäblich eine Abkürzung für if err != nil { return ..., err } .

Existierende Sprachkonstrukte lösen 3 und 4. Wir können bereits mit if err != nil { ... } auf einen Fehler _ ignorieren.

Dies lässt uns mit 2 (einen dekorierten Fehler zurückgeben). Der Vorschlag von try() schlägt vor, eine defer-Anweisung zu verwenden, um den Fehler zu dekorieren, oder wenn jeder Fehler anders dekoriert werden muss, dann verwenden Sie ein Standardkonstrukt if err != nil { ... } .

Die Begründung wird in diesem Teil des Vorschlags gut erläutert:

Unsere erste Iteration dieses Vorschlags wurde von zwei Ideen aus Key Parts of Error Handling inspiriert, die darin besteht, einen integrierten anstelle eines Operators und eine normale Go-Funktion zur Behandlung eines Fehlers anstelle eines neuen Fehlerbehandlungs-Sprachkonstrukts zu verwenden. Im Gegensatz zu diesem Beitrag hatte unser Fehlerhandler zur Vereinfachung die feste Funktionssignatur func(error) error . Der Fehlerhandler würde von try aufgerufen, wenn ein Fehler auftritt, kurz bevor try von der einschließenden Funktion zurückgegeben wird. Hier ist ein Beispiel:

handler := func(err error) error {
        return fmt.Errorf("foo failed: %v", err)  // wrap error
}

f := try(os.Open(filename), handler)              // handler will be called in error case

Dieser Ansatz ermöglichte zwar die Spezifikation von effizienten benutzerdefinierten Fehlerhandlern, aber er hat auch viele Fragen aufgeworfen, auf die es offensichtlich keine richtigen Antworten gab: Was sollte passieren, wenn der Handler bereitgestellt wird, aber null ist? Sollte try Panik geraten oder es als abwesenden Fehlerhandler behandeln? Was passiert, wenn der Handler mit einem Nicht-Null-Fehler aufgerufen wird und dann ein Null-Ergebnis zurückgibt? Bedeutet dies, dass der Fehler "abgebrochen" ist? Oder sollte die einschließende Funktion mit einem Null-Fehler zurückkehren? Es war auch nicht klar, ob die Zulassung eines optionalen Fehlerhandlers dazu führen würde, dass Programmierer die richtige Fehlerbehandlung völlig ignorieren. Es wäre auch einfach, überall eine ordnungsgemäße Fehlerbehandlung durchzuführen, aber ein einziges Auftreten von try verpassen. Und so weiter.

In der nächsten Iteration wurde die Möglichkeit entfernt, einen benutzerdefinierten Fehlerhandler bereitzustellen, anstatt defer für den Fehlerumbruch zu verwenden. Dies schien ein besserer Ansatz zu sein, da Fehlerbehandlungsroutinen im Code viel sichtbarer wurden. Dieser Schritt eliminierte alle Fragen zu optionalen Funktionen als Fehlerhandler, erforderte jedoch, dass die Fehlerergebnisse benannt wurden, wenn ein Zugriff darauf erforderlich war (wir entschieden, dass dies in Ordnung war).

[…]

Wenn wir später feststellen, dass eine explizit bereitgestellte Fehlerbehandlungsfunktion oder ein anderer zusätzlicher Parameter eine gute Idee ist, ist es trivial möglich, dieses zusätzliche Argument an einen try-Aufruf zu übergeben.

Sind Sie mit dieser Argumentation nicht einverstanden?

Ich kenne diesen Teil des Vorschlags und stimme ihm nicht zu, denn die Tatsache, dass Fragen aufgrund der Idee, einen Fehlerhandler zu übergeben, geöffnet wurden, bedeutet nicht, dass wir eine solche Funktion nicht benötigen. Es bedeutet nur, dass wir gut nachdenken müssen, um diese Fragen vernünftig zu beantworten.

Darüber hinaus behandeln wir jetzt alle 4 Fehleranwendungsfälle mit einer if err != nil Anweisung, die ein weit verbreitetes, konsistentes Go-Idiom ist. Wenn wir nur try() für Fall 1 und möglicherweise für Fall 2 verwenden, bedeutet dies, dass der Code für die Fehlerbehandlung in if aufgeteilt wird, wenn uns der Aufwand für das Umschließen von Fehlern in einer Defer-Anweisung nichts ausmacht. try inkonsistent, und wenn sich die Fehlerbehandlung ändert, müssen wir zwischen dem einen und dem anderen wechseln.

Eine leistungsfähigere Version von try() , die in allen Fällen verwendet werden könnte, würde es uns ermöglichen, try() fast immer zu verwenden, und dies würde dann das neue, konsistente Idiom für die Fehlerbehandlung für Go werden.

Mit try() wie es jetzt ist, ist es jedoch nicht weit genug anwendbar, und es gibt einfach nicht genug Komfort, um die oben erwähnte Inkonsistenz in der Fehlerbehandlung in unsere Codebasen einzuführen. Deshalb fühle ich mich von dem aktuellen try() Vorschlag überfordert und denke, nichts zu tun ist besser.

@beoran Ich denke, die Fälle 1 und 2 haben gemeinsam, dass sie einen Fehler von der Funktion zurückgeben (bzw. ohne und mit Dekoration), während die Fälle 3 und 4 dies nicht tun (jeweils auf einen Fehler reagieren und einen Fehler ignorieren). Ich denke, der Fokus von 'try()' liegt auf den Fällen 1 und 2.

Was wäre, wenn der Vorschlag von try() verbessert werden könnte, um die Fälle 1 und 2 zu behandeln, indem eine optionale Handlerfunktion akzeptiert wird? Dies erfordert natürlich, vernünftige Antworten auf die im Vorschlag aufgeführten Fragen zu finden, aber es scheint handhabbar. Würden Sie so etwas unterstützen?

Hier bin ich raus. Wenn dies der Fall ist, möchten wir, dass Benutzer Fehler überprüfen, sollten wir wahrscheinlich geprüfte Fehler hinzufügen (so ähnlich wie geprüfte Ausnahmen). Auf diese Weise sind wir so eindeutig wie möglich und der Benutzer weiß, dass er alle Fehler überprüft.

Im Ernst, wenn ich derjenige wäre, der diese Entscheidungen trifft, würde ich wirklich gerne den ? Operator von Kotlin oder so etwas wie das rostige Verhalten von unwrap() umfunktionieren.

Beides wäre meiner Meinung nach eine Verbesserung:

getFile()?.getPath()?.toString()

wo Sie nil zurückbekommen, wenn auf dem Weg ein Fehler aufgetreten ist oder

get_file().unwrap().get_path().unwrap().lossy_to_string()

wo es auf halbem Weg explodiert, wenn ein Fehler auftritt. Rust geht mit dem zweiten um, indem es eine ausdrucksstarke match Funktion hat, mit der Sie eine umfassende Suche nach den Fehlern durchführen und jeden einzeln behandeln können, wobei alle einen Wert in irgendeiner Form in der Kette zurückgeben.

Am 02.07.19 schrieb Nicolas Grilly [email protected] :

@beoran Ich denke, die Fälle 1 und 2 haben gemeinsam, dass ein Fehler von der zurückgegeben wird
Funktion (jeweils ohne und mit Dekoration), während Gehäuse 3 und 4
don't (bzw. auf einen Fehler reagieren und einen Fehler ignorieren). Ich denke 'versuchen()'
Der Fokus liegt auf den Fällen 1 und 2.

Ich bin etwas enttäuscht, dass mein Vorschlag nicht mehr diskutiert wird
dass es der "Rückgabe"-Teil von "Fehlerrückgabe" ist, der sein muss
adressiert, nicht der "Fehler"-Teil.

Andererseits hätte ich vielleicht einen offizielleren Ansatz verfolgen sollen. Ich bin
einfach nicht gut darin, Vorschläge zu formulieren, ich neige dazu, alles durchzugehen
Platz.

Die Fälle 1 und 2 sind meiner Meinung nach besser mit einem "Fail"-Befehl gedient
Stichwort, das (nach einiger Eingewöhnung) deutlich anzeigt, a
Änderung im Programmablauf und die keiner
unbequeme Tradition in Bezug auf ihre volle Syntax.

Was aber daraus folgt, ob es uns gefällt oder nicht, ist das
die Positionierung von "err" als letzter Rückgabeparameter ist bald zu
Gesetz statt Konvention werden. Das ist eines von vielen "unbeabsichtigten" oder
"Kollaterale" Konsequenzen, die berücksichtigt werden müssen.

Es wird viele andere solcher Konsequenzen geben, von minimal bis zu
katastrophal, einige, die nur opportunistisch huckepack auf die
Vorschlag. Ich persönlich würde auf Nummer sicher gehen, das tue ich
verstehen, warum das Go-Team und insbesondere Robert suchen
Unterstützung und zweifellos sind verletzt, dass sich der Widerstand als so herausgestellt hat
groß.

Es ist ein Zusammenprall politischer Ideologien, vielleicht müssen wir viel graben
tiefer, um die wahren Wurzeln zu suchen, die etwas gärtnerische Aufmerksamkeit erfordern.

@lootch Haben Sie ein besonderes try funktioniert? Ist das nicht schon eine De-facto-Konvention?

(Nebenbei gesagt, wir sind nicht "verletzt" über den Widerstand - meistens verblüfft über die übergroße Reaktion.)

@griesemer Entschuldigung für die alte Auto-Analogie, aber die Vorschläge fühlen sich an wie "Wir haben beschlossen, dass wir entweder alle Benzinautos mit einem Dieselkraftstoffsystem oder alle Benzinautos mit einer Batterie und einem Elektromotor versehen werden." Die Fehlerbehandlung von Go ist gut genug. Ich befürchte, dass der Mehrwert geringer sein wird als die Kosten, einschließlich neuer Fußwaffen und mentaler Mehrkosten.

Ich möchte nur einen kurzen Kommentar schreiben, der besagt, dass ich, nachdem ich viel von Go geschrieben habe (ich benutze Go seit seiner ersten öffentlichen Veröffentlichung im Jahr 2009 - siehe meinen Github), eine Verbesserung der Ergonomie rund um die Fehlerbehandlung begrüßen würde

Ich habe mich genug darüber geärgert, dass ich vor ein paar Jahren tatsächlich etwas Zucker um Panik und Genesung geschrieben habe, damit sie (meistens) wie ungeprüfte Ausnahmen funktionieren: https://hackthology.com/exceptions-for-go-as-a-library. html . Tatsächlich ist es eine schlechte Idee, diese Wrapper zu verwenden, da sich die Community dort befindet. Ich glaube jedoch weiterhin, dass die Verbesserung der Ergonomie rund um die Fehlerbehandlung ein Gewinn ist.

Es scheint verrückt, dass die Leute leidenschaftlich dafür argumentieren, eine extrem ausführliche, aber wenig hilfreiche Methode zur Verbreitung von Fehlerzuständen beizubehalten. Ich habe in meinem Code (und im Code anderer) echte Fehler geschrieben und gefunden, bei denen Leute die if err != nil Bedingung durcheinander gebracht und Fehler erstellt haben. Es gibt wirklich keinen Grund dafür, dass diese Fehler jemals geschrieben werden. Statische Überprüfungen können helfen, diese Fehler jedoch nicht beseitigen.

Ich unterstütze die Vorschläge zur Verbesserung der Ergonomie rund um die Fehlerbehandlung.

Der Vorschlagsprozess ist für, um https://golang.org/s/proposal zu zitieren, „Änderungen vorschlagen zu gehen“.
Dieses Problem schlägt keine Änderung vor, es handelt sich also wirklich um einen Kategoriefehler.
Wenn jemand ein Angebot mit dem Titel "Auswahl allein lassen" eingereicht hat, würden wir es einfach als kein Angebot schließen.

Aber in Wirklichkeit ist dieses Thema nur eine Erweiterung der Diskussion über andere Themen wie #32437, daher werde ich es als einen guten Ort offen lassen, um über "Nichts tun" zu diskutieren.

Ich habe es mit Proposal-Hold markiert, um darauf hinzuweisen, dass es hier keine spezifische Entscheidung gibt, sondern eher eine Meta-Entscheidung.

Seite: Es kommt mir seltsam vor, dass diese Ausgabe 1335 positive Stimmen hat, während der try Vorschlag (#32437) _nur_ 279 negative Stimmen hat.

Der Versuchsvorschlag war gesperrt, als ich darauf aufmerksam wurde, sodass ich ihn nicht ablehnen konnte. Ich gehe davon aus, dass deshalb auch dieser Vorschlag von Anfang an eingereicht wurde.

Am 02.07.19 schrieb Robert Griesemer [email protected] :

@lootch Haben Sie ein besonderes Interesse daran, dass Fehler der letzte sein
Rückgabeparameter, damit try funktioniert? Ist das nicht schon de-facto
Konvention?

Ich tue das in dem Sinne, dass, wenn das "de facto" in Stein gemeißelt wurde, dann
diese Tür sollte aufgeschwungen werden und "Fehler" zu einem optionalen, besonderen machen
Argumenttyp und akzeptiere das, denn für jedes richtige Ergebnis gibt es
sind meistens viele falsche und das können sie auch sein
in besonderer Weise angesprochen.

Sobald die Idee, dass die "Rückgabe"-Anweisung untersucht werden muss,
tiefer (und der Versuchsvorschlag scheint darauf hinzuweisen)
Richtung), dann ist die Fehlerbedingung nicht mehr "nur ein Wert, und
Der Ansatz von Go beginnt, dem besten Kartenhaus zu ähneln, das gebaut wurde
Datum, aber dennoch ein Kartenhaus.

Ich bin ein widerwilliger Benutzer einiger der klügeren Tricks von Go (ich habe
an anderer Stelle erwähnt, dass x++ und x-- nicht vorhanden sind - meistens rutsche ich aus
manchmal auf - in meinem Code), also wird "try" für mich einer davon bleiben,
aber ich habe grundsätzlich keine Einwände gegen seine Einführung, ich fühle nur
dass so viel gesagt wurde und trotzdem nicht alle möglichen Nachteile
wurden enthüllt (die unbeabsichtigten Folgen dessen, was ich werden sehe)
eine eventuelle politische Entscheidung). Das auf die Probe zu stellen kann gut sein
oder schlecht.

Was ich sehe ist, dass Panik/Erholung einen schlechten Ruf hat und die Ungerechtigkeit davon
kommt zurück, um uns zu verfolgen. Auch hier muss ich beides noch verwenden und ich
fürchte den Tag, an dem ich es muss, weil ich nicht der schärfste Keks im Glas bin
und ich werde es sehr schwer finden, aber Panik/Erholung wurde gefördert
für die seltenen Gelegenheiten, wo es geeignet ist (keiner von ihnen behandelt von
Rob Pike in seiner wunderbaren Präsentation über Fehler, die einfach zurückkommen
Werte, wenn ich mich recht erinnere), wäre das alles viel klarer
Go behandelt Fehler bereits so gut, wie es ohne Notwendigkeit zu erwarten ist
um den Sumpf möglicher Optionen zu erkunden, die darüber hinaus verfügbar sind
Grenzen.

Das heißt, es macht genauso viel Sinn, es auf die Probe zu stellen, es ist, nachdem
alles, eine optionale Funktion. Aber der eine Nebeneffekt ist, was das
Es geht um den Austausch: Welche Folgen hat "de facto"
zwingende Fehlerbehandlungsfunktionen, die ein Argument vom Typ "Fehler" haben
am Ende ihrer Rückgabeparameterliste? Wie wird sich das ändern
Wahrnehmung, dass "Fehler nur Werte sind"? Wie passt es zusammen?
das jetzt viel analogere "Komma-OK"-Paradigma? Was soll das noch?
neues Prinzip entstehen lassen?

Vor allem denke ich, der Berg, der aus diesem Maulwurfshügel besteht, ist
ein Zeichen dafür, dass Go einen lebensverändernden Meilenstein erreicht hat. Schon fast
sicherlich, Annahmen, die 2013 zutrafen, sind möglicherweise nicht mehr länger
halt. Nicht dass das für irgendjemanden neu wäre, aber es deutet darauf hin, dass Go2 möglicherweise
nicht so schön, abwärtskompatibel mit Go1 zu sein, wie es bisher war
zu fest vorgeschlagen (meiner Meinung nach).

@lootch Danke für deine ausführliche Antwort. Es ist in der Tat sehr schwierig, eine vorhandene Sprache rückwärtskompatibel zu erweitern, und daher stimme ich Ihnen zu, dass man die Dinge vielleicht anders machen könnte, wenn diese Rückwärtskompatibilität nicht erforderlich wäre. Wir werden bald (sobald Module alltäglich geworden sind) in der Lage sein, Sprachänderungen vorzunehmen, die keine strikte Abwärtskompatibilität erfordern.

Wie Sie sagen, ist try ein optionaler Mechanismus, und - ich denke, es lohnt sich, ihn zu wiederholen - trotz allem Drumherum ein sehr einfacher Mechanismus, der mit der vorhandenen Go-Funktionalität leicht erklärt werden kann - es ist nur so, dass wir t schreiben try als Funktion in Go (und Generika würden auch nicht helfen). Wenn wir könnten, bin ich mir sicher, dass es durchaus üblich wäre, Hilfsfunktionen wie try für die Fehlerbehandlung in vorhandenem Code zu sehen. Es ist schließlich genau das, worum es in Rob Pikes Rede über Fehler als Werte geht: es geht um das Programmieren mit Fehlern.

Es überrascht mich immer noch sehr, dass es einen solchen Aufruhr über das Hinzufügen einer ziemlich kleinen Hilfsfunktion gibt, die jeder ignorieren kann, aber die Leute erwägen ernsthaft bedeutende Sprachänderungen wie on error die wirklich stark auf einen bestimmten Stil hindeuten der Fehlerbehandlung in der Sprache.

Danke nochmal für deinen Input. Das ist alles sehr interessant.

Ich bin mir nicht sicher, ob dieser Vorschlag der richtige Ort ist, um darüber zu diskutieren, aber da es in diesem Thread bereits eine lebhafte Diskussion über das Schlüsselwort try gibt, werde ich es vorerst nur als thematisch betrachten :)

Ich frage mich, ob die Google-Leute bereit wären, das Schlüsselwort try in ihrem internen Golang-Repository zu implementieren und anschließend die vorhandenen Google-Codebasen so zu konvertieren, dass dieses Schlüsselwort verwendet wird. Wenn Sie es nur intern behalten, können Sie es leicht rückgängig machen (dh die Belastung liegt bei einem einzelnen Unternehmen und nicht bei der gesamten Gemeinschaft).

Dies würde es ermöglichen, eine kleine Fallstudie zu dieser Funktion in einer realen High-Sloc-Codebasis durchzuführen. Facebook hat in der Vergangenheit etwas Ähnliches mit PHP-Funktionen gemacht, und auf diese Weise konnten sie bestimmte Funktionen verfeinern, bevor sie sie der Community insgesamt vorschlugen.

Wenn Sie eine Fallstudie darüber schreiben, wie das Schlüsselwort try _in der Praxis_ verwendet wurde und welchen Mehrwert es im wirklichen Leben hat, könnte dies ein überzeugendes Argument liefern. Wenn es (hypothetisch) keinen Wert für die reale Welt liefern würde, wäre es auch wertvoll, dies zu wissen. Manchmal sieht etwas auf dem Papier richtig gut aus, funktioniert aber in der Praxis nicht – oder umgekehrt.

@Freeaqingme Wir haben bereits eine vorläufige CL erstellt, die zeigt, wie try in der Standardbibliothek aussehen könnte: https://go-review.googlesource.com/c/go/+/182717 (es kann falsch sein Positives, aber wenn ja, sind sie sehr selten). Wir überlegen vielleicht, ein Tool zu entwickeln, das eine Konvertierung von Codebasen in beide Richtungen ermöglicht.

Sie werden auch ermutigt, tryhard in Ihrer Codebasis zu verwenden und Bericht zu erstatten.

Vielen Dank.

@griesemer Ich habe mich vielleicht nicht klar

Natürlich kann ich es auch selbst anwenden und auf meinen eigenen Codebasen verwenden (und das könnte ich auch). Aber ich bin nur ein einzelner Entwickler mit einer relativ kleinen Codebasis, daher wäre dies nicht so repräsentativ, wenn viele Google-Mitarbeiter es verwenden würden.

Persönlich bin ich bei dieser Funktion immer noch am Zaun. Einerseits schätze ich die Tatsache, dass ich mir jedes Mal ein paar Tastenanschläge sparen kann. Aber manchmal bin ich vielleicht faul (ich bin schließlich ein Mensch) und verschachtele einfach so viele try-Anweisungen wie möglich. Jetzt werde ich einfach etwas disziplinierter sein, wenn dieses Feature vorhanden wäre. Aber selbst wenn ich es wäre, besteht immer noch die Möglichkeit, dass externe Bibliotheken dieses Schlüsselwort überbeanspruchen / missbrauchen, während meine Codebasis darunter leidet (in Bezug auf die Debugbarkeit und Erweiterbarkeit dieser Bibliotheken).

@Freeaqingme Verstanden . Wir könnten sicherlich try über interne Repos ausführen. Ich bin mir nicht sicher, ob wir konvertieren und wieder konvertieren können - hier sind erhebliche Kosten verbunden. Außerdem konnten wir zu diesem Test nur aggregierte Berichte (Statistiken) erstellen, da wir keine internen Details melden könnten. Das heißt, die Community hätte keine einfache Möglichkeit, unsere Behauptungen zu überprüfen. Schließlich ist die Codebasis von Google möglicherweise nicht repräsentativ.

Aber danke, Punkt genommen.

@griesemer Ich weiß zu schätzen, dass es ein kostspieliges Unterfangen sein kann. In diesem Fall würde ich es nicht tun. Wenn es nur darum geht, Ihr Tryhard-Projekt anzuwenden, können die Kosten begrenzt sein. Aber das muss wirklich ein Googler (was ich nicht bin) feststellen.

Ihren Angaben zufolge können Sie keine Berichte zu einzelnen Google-Projekten oder zur internen Infrastruktur erstellen. Ein paar Anekdoten könnten jedoch vielleicht geteilt werden. Aber was ich persönlich viel wertvoller finden würde, sind einige Google-Mitarbeiter, die darüber berichten, wie es für sie gelaufen ist. Ohne auf Einzelheiten einzugehen, würde ich Aussagen wie "Ich habe X erwartet, aber als ich auf Fälle wie A und BI stieß, dass Y gefunden wurde" sehr wertvoll finden. Ich würde feststellen, dass diese Art von Berichten nicht überprüft werden muss.

Schließlich ist die Codebasis von Google möglicherweise nicht repräsentativ.

Es kann sein, es kann nicht sein. Aber es gibt viele Leute, die bei Google arbeiten, daher würde ich annehmen, dass der größte Teil der Codebasis nicht darauf basiert, wie eine einzelne Person sich entschieden hat, sie zu schreiben, sondern eher ein Höhepunkt der Beiträge vieler verschiedener Mitwirkender (Mitarbeiter) ist. In dieser Hinsicht erwarte ich, dass die Dinge so repräsentativ sind, wie es nur geht. Es gibt wahrscheinlich keine 100% repräsentative Codebasis.

Belassen Sie es so wie es ist, bis wir eine bessere Lösung gefunden haben, das try ist nicht das, wonach wir gesucht haben. Keine Notwendigkeit, voreingenommene Maßnahmen zu ergreifen, nehmen Sie sich Zeit Go Authors. Soweit ich täglich mit Gophern auf der ganzen Welt spreche, bin ich fest davon überzeugt, dass die Mehrheit der Go-Community den try Vorschlag nicht annimmt.

Die Einführung einer neuen Syntax bedeutet, dass jeder seine Go-Version aktualisieren muss. Ich verwende Go 1.10 immer noch, weil mein Workflow darauf basiert, dass ich Dinge go get und sie dann über die Befehlszeile verwenden kann (mein GOPATH ist in meinem PATH ). . Ich hatte vor kurzem ein Problem beim Versuch, die Bibliothek eines anderen go get verwenden, die Module verwendet. Ich habe eine Fehlermeldung erhalten, dass .../v2 nicht verfügbar war. Dies bedeutet, dass der Code bereits aufgeteilt ist (denken Sie an Python 2 und 3). Für mich gibt es eine Welt vor Go 1.11 und nach Go 1.11. Das ist sehr ärgerlich und die Einführung einer neuen Syntax für etwas, das genauso gut funktioniert wie die Fehlerbehandlung in Go, ist überhaupt kein guter Kompromiss. Es führt zu mehr Fragmentierung.

Am 04.07.19 schrieb gonutz [email protected] :

Die Einführung einer neuen Syntax bedeutet, dass jeder sein Go aktualisieren muss
Ausführung. Ich verwende Go 1.10 immer noch, weil mein Workflow auf der Tatsache basiert
dass ich go get Dinge kann und sie dann über die Befehlszeile verwenden kann (my
GOPATH ist in meinem PATH ). Ich hatte vor kurzem ein Problem beim Versuch, go get
die Bibliothek einer anderen Person, die Module verwendet. Ich habe einen Fehler erhalten, dass .../v2 war
Nicht verfügbar. Dies bedeutet, dass der Code bereits aufgeteilt ist (denken Sie
Python2 und 3). Für mich gibt es eine Welt vor Go 1.11 und nach Go 1.11.
Das ist sehr ärgerlich und führt eine neue Syntax für etwas ein, das funktioniert
sowie die Fehlerbehandlung in Go ist überhaupt kein guter Kompromiss. Es
führt zu mehr Fragmentierung.

Wenn es dich tröstet, ich bin in genau der gleichen Situation gegenüber
Go-Module. Ich habe nicht die Zeit und Gelegenheit gefunden, mich kennenzulernen
mit ihnen, also bleibe ich auch bei Go1.10. Vielleicht sollte das so sein
eine Umfrage, die es wert ist.

Lucio.

--
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an:
https://github.com/golang/go/issues/32825#issuecomment -508372318

--
Lucio De Re
2 Piet Retief Straße
Kestell (Ost-Freistaat)
9860 Südafrika

Tel.: +27 58 653 1433
Handy: +27 83 251 5824
FAX: +27 58 653 1435

Ich bin ein neuer Golang-Entwickler (lerne immer noch etwas über go). Ich denke, dass die aktuelle Fehlerbehandlung gut ist, weil sie uns hilft, Fehler leicht zu handhaben. Als Android-Entwickler denke ich, dass try-catch schwieriger ist, unseren Fehler zu verwalten als if err != nil{ } in Golang. Und ich denke, explizite Fehlerbehandlung ist immer besser als implizite Fehlerbehandlung.

PS. Entschuldigung für meine Sprache.

leave it alone

Es ist nicht kaputt....

Liebe das Mem, @Daniel1984 :-)

Übrigens lässt der Vorschlag try if err != nil allein; es gibt Ihnen nur eine zusätzliche Option, wo es sinnvoll ist.

Ich bin der Meinung, dass try nicht enthalten sein sollte. Zur Aufnahme von try :

Profi

  • Programmierer verringern die Anzahl der Tastenanschläge, die sie machen.
  • Programmierer können eine Abkürzung für die Rückkehr von der aktuellen Funktion à la Makro haben.
  • Es ist nicht erforderlich.
  • Es würde allgemein verwendet werden.
  • Es ist klar, wo die Magie stattfindet (im Gegensatz zu Javas throws ).
  • Die Augen werden nicht mehr glasig, wenn man das Meer der nil Schecks betrachtet.
  • Funktioniert am besten mit einfachen Implementierungen.

Kon

  • try fügt eine Duplikatmethode für eine vorhandene Operation hinzu.
  • Dass try von der aktuellen Funktion zurückkehrt, ist unerwartet, AKA mehr Magie.
  • Es fügt der Fehlerprüfung Inkonsistenzen hinzu.
  • Programmierer ohne Go-Erfahrung werden es nicht verstehen.
  • Ändert nichts an der Fehlerbehandlung.
  • Weniger Klarheit (Nichtübereinstimmung zwischen Funktionsrückgabe und Ausdruckswert).
  • Schwer zu beschreiben, was bei try passiert, in Worten.

@griesemer genau das mag ich nicht. Die Dinge sollten einfach sein, ich möchte der Sprache keine Komplexität hinzufügen, nur um zwei Möglichkeiten zu haben, dasselbe zu erreichen. Es gibt Muster, um diese if err != nil Ausführlichkeit zu vermeiden. https://www.ardanlabs.com/blog/2019/07/an-open-letter-to-the-go-team-about-try.html

Der Go2-Vorschlag #32437 fügt der Sprache eine neue Syntax hinzu, um das if err != nil { return ... } Boilerplate weniger schwerfällig zu machen.

Es gibt verschiedene alternative Vorschläge: #32804 und #32811, da das Original nicht überall beliebt ist.

Um eine weitere Alternative in den Mix zu werfen: Warum nicht so belassen

Mir gefällt die explizite Natur des if err != nil Konstrukts und als solche verstehe ich nicht, warum wir dafür eine neue Syntax brauchen. Ist es wirklich so schlimm?

Sehr viel dies. Expliziter Code ist korrekter Code. Die Schrecken, die ich mit Ausnahmehandlern gesehen habe, reichen aus, um dieses schreckliche, unlesbare Konstrukt für immer zu verlassen.

Es scheint eine massive Verzögerung zu geben, da die Leute nur noch Kommentare abgeben
jetzt und man konnte sich den Eindruck nicht erwehren, dass es frisch ist
ihnen Neuigkeiten.

Auch das gilt es zu berücksichtigen. Die Weinrebe ist eindeutig nicht so
schnell wie man meinen mag.

Lucio.

Und ändern Sie gofmt, um einzeilige if-Anweisungen zuzulassen, die einfache Handballen und Fehler bis hin zu den
aufrufende Funktion. Das würde viel Unordnung beseitigen.

Anstatt von:

err = meineFunktion()
wenn err != nil {
Rückgabefehler
}

Erlauben:

err = meineFunktion()
if err != nil { return err}

Übrigens lässt der try-Vorschlag if err != nil allein; es gibt Ihnen nur eine zusätzliche Option, wo es sinnvoll ist.

Diese genaue Begründung ist, wie Go zu einem weiteren C++, C#, Scala, Kotlin usw. wird. "Nun, Sie brauchen es nicht zu verwenden, wenn Sie nicht wollen", ist die Art und Weise, wie funktionsaufgeblähte Sprachen erstellt werden.

Bearbeiten - das ist möglicherweise falsch rübergekommen. Ich sage nicht, dass try Go in eine aufgedunsene Sprache verwandeln wird. Ich sage, dass die Begründung hier ein bisschen fehlerhaft ist

@deanveloper Sie haben ein eindeutiges Beispiel für schwer verständliches Fehlerverhalten mit "try":
https://github.com/golang/go/issues/32437#issuecomment -498932961

Auch wenn es sich ein wenig wiederholt, stimme ich OP zu.
Darüber hinaus führen imo alle vorgeschlagenen Alternativen nutzlose Komplexität ein.

Es ist einfach cool, die geschweiften Klammern wegzulassen, wenn Sie nur eine Linie im Geltungsbereich haben

@verrückt werden

Dies bedeutet, dass der Code bereits aufgeteilt ist (denken Sie an Python 2 und 3). Für mich gibt es eine Welt vor Go 1.11 und nach Go 1.11.

Ich bin ein langjähriger Python-Programmierer und die angebliche "Aufspaltung", die Sie in Bezug auf Go-Module erwähnen, ist nichts im Vergleich zu der Katastrophe der Migration von Python 2 zu Python 3.

Das ist sehr ärgerlich und die Einführung einer neuen Syntax für etwas, das genauso gut funktioniert wie die Fehlerbehandlung in Go, ist überhaupt kein guter Kompromiss. Es führt zu mehr Fragmentierung.

try ist eine im Angebot integrierte Funktion. Es ist vollständig abwärtskompatibel. Wenn Ihr Code bereits try als Kennung verwendet, wird Ihre Kennung das integrierte try überschatten.

@pongsatornw

Ich denke, dass die aktuelle Fehlerbehandlung gut ist, weil sie uns hilft, Fehler leicht zu handhaben. Als Android-Entwickler denke ich, dass Try-Catch schwieriger ist, unseren Fehler zu verwalten, als wenn err != nil{ } in Golang ist. Und ich denke, explizite Fehlerbehandlung ist immer besser als implizite Fehlerbehandlung.

Haben Sie den Vorschlag vollständig gelesen? try ist nur eine eingebaute Funktion, die hilft, die Wiederholung von if err != nil { return ..., err } . Die allgemeine Logik der Fehlerbehandlung in Go bleibt gleich. Es ist immer noch explizit, Fehler sind immer noch Werte und es gibt keinen Try-Catch (auch als Ausnahmen bezeichnet).

@kroppt

  • try fügt eine Duplikatmethode für eine vorhandene Operation hinzu.

try faktorisiert nur sich wiederholenden Code. Genauso append mit append aufzurufen.

  • Es fügt der Fehlerprüfung Inkonsistenzen hinzu.

Sie können ein Slice "manuell" mit [...:...] oder mit append , je nachdem, was Sie tun. Es gibt keine Inkonsistenz. Sie sind nur unterschiedliche Werkzeuge für unterschiedliche Aufgaben. Dasselbe gilt für Fehler, mit einem einfachen if err != nil { ... } oder mit try , je nach Aufgabe.

  • Dass try von der aktuellen Funktion zurückkehrt, ist unerwartet, AKA mehr Magie.

Es ist unerwartet, weil es neu ist. Wir gewöhnen uns daran, wenn wir es mehr benutzen. Und ich glaube nicht, dass es Magie ist; Die Spezifikation des Versuchs ist sehr einfach.

  • Programmierer ohne Go-Erfahrung werden es nicht verstehen.
  • Es ist schwer in Worten zu beschreiben, was bei try passiert.

Programmierer ohne Go-Erfahrung verstehen chan , defer , 'go , iota , Panik , erholen , <- , type assertions, and many other things either without reading the documentation. try` ist im Vergleich zu den meisten anderen einfach.

  • Ändert nichts an der Fehlerbehandlung.

Vielleicht ist das eine gute Sache, laut Gophern, die bitten, if err != nil Ruhe zu lassen ;-)

@marcopeereboom

Expliziter Code ist korrekter Code. Die Schrecken, die ich mit Ausnahmehandlern gesehen habe, reichen aus, um dieses schreckliche, unlesbare Konstrukt für immer zu verlassen.

try hat absolut nichts mit Ausnahmebehandlung zu tun. Haben Sie den vollständigen Vorschlag gelesen? Es gibt nichts Vergleichbares zur Ausnahmebehandlung wie beispielsweise in Java oder Python. try ist explizit. Fehler müssen in Funktionssignaturen erwähnt werden. Fehler müssen auf der Anrufseite behandelt werden. Es gibt kein Abwickeln des Stapels. Usw.

@gale93

Es ist einfach cool, die geschweiften Klammern wegzulassen, wenn Sie nur eine Linie im Geltungsbereich haben

Ich denke, die meisten Gophers hatten den gleichen Gedanken, und ich habe im Issue Tracker oft ähnliche Vorschläge gelesen. Aber es ist eine viel größere Änderung als try . Es wäre nicht sinnvoll, dies nur für die Anweisung if zu tun. Sie müssten dies also überall dort ändern, wo ein Block akzeptiert wird. Ohne { das den Anfang des Blocks markiert, müssen Sie eine Möglichkeit angeben, das Ende des bedingten Ausdrucks zu begrenzen. Sie müssen die Grammatik, den Parser, gofmt usw. aktualisieren. Dies würde die Sprachoberfläche vollständig verändern.

@ngrilly
Moderation und einfache Sprache sind wichtig.

Einige der von Ihnen verwendeten Argumente könnten eine große Änderung der Spezifikation rechtfertigen. Hier gibt es nicht nur positive Punkte.

Ich bewerte die Entscheidung danach, ob sie helfen oder schaden würde, nicht unbedingt eine bestimmte Philosophie vollständig umzusetzen. Sie haben Recht, dass einige Dinge in der Spezifikation gegen bestimmte Prinzipien verstoßen, auf denen go gegründet wurde, aber es geht nur um Mäßigung. Diese Änderung hat für mich keine positive Wirkung, um die negativen zu tolerieren.

Hallo @kroppt ,

die sprache einfach zu halten ist wichtig

Ich stimme zu und ich denke, wir alle streben danach.

Ich bewerte die Entscheidung danach, ob sie helfen oder schaden würde, nicht unbedingt eine bestimmte Philosophie vollständig umzusetzen.

Ich denke, wir alle bewerten try basierend auf Nutzen und Kosten. In der Diskussion geht es darum, einen faktenbasierten Konsens über diese Vorteile und Kosten zu definieren und zu finden, was ich in meinem vorherigen Kommentar versucht habe.

Sie haben Recht, dass einige Dinge in der Spezifikation gegen bestimmte Prinzipien verstoßen, auf denen go gegründet wurde

In den letzten Jahren habe ich fast alles gelesen, was das Go-Team über Go und sein Design veröffentlicht hat, und verstehe nicht, was Sie meinen. Ich glaube nicht, dass der Vorschlag von try gegen die Grundprinzipien von Go verstößt.

@ngrilly
https://talks.golang.org/2012/splash.article beschreibt einige der Konzepte, die hinter go – unter anderem Klarheit und Einfachheit. Ich denke, das ist der Konflikt, den einige von uns mit dieser neuen Änderung sehen. Es ist einfacher, aber weniger klar. Mir scheint, dass der Gewinn an Einfachheit geringer ist als der Verlust an Klarheit. Vielleicht liege ich falsch und bin nur vorsichtig.

@kroppt Ich habe diesen Artikel Dutzende Male gelesen

@ngrilly
Was du beschreibst steht in meinem "Profi"-Bereich:

  • Programmierer verringern die Anzahl der Tastenanschläge, die sie machen.
  • Programmierer können eine Abkürzung für die Rückkehr von der aktuellen Funktion à la Makro haben.
  • Die Augen werden nicht mehr glasig, wenn man das Meer von nil Schecks betrachtet.

Auch hier sehe ich keinen Sinn darin, die universelle Anwendung eines Prinzips zu erwähnen, wenn wir es hier nicht universell anwenden.

Ich stimme der Idee nicht zu, dass Versuch den Code verschleiert

Der Sinn der Änderung besteht darin, Code zu verschleiern/auszublenden/vereinfachen/darzustellen – andernfalls würden wir den ursprünglichen Fehlerprüfblock sehen. Die Frage ist, ob es die Bedeutung weniger deutlich macht.

Ich denke, dass go ursprünglich eine großartige Balance zwischen Einfachheit gefunden hat, bis zu dem Punkt, an dem es zur Klarheit beigetragen hat, anstatt sie zu beeinträchtigen. Ich kann nicht erklären, wie sie es gemacht haben, aber try meiner Meinung nach nicht.

Ich denke, wir sollten die Ausführlichkeit nicht als Problem betrachten. Code muss von Menschen gelesen und verstanden werden – deren Zeit teurer ist als die von Computern – und _Verstehen_ ist in der Regel der schwierige und zeitaufwändige Teil.

Ich finde, dass die Einrückungsstruktur der go-Fehlerbehandlung mir hilft, zu verfolgen, was vor sich geht. Jede Fehlerprüfung ist explizit. Die meisten nicht behandelten Fehler sind ebenfalls explizit. Dadurch ist der Code für mich schnell verständlich.

Ich würde auch denken, dass if err != nil Checks zwar mühsam erscheinen können, ich sie aber nicht wirklich _typisieren_ muss. Ich lasse es einfach von meinem Redakteur machen.

@kroppt

Der Sinn der Änderung besteht darin, Code zu verschleiern/auszublenden/vereinfachen/darzustellen – andernfalls würden wir den ursprünglichen Fehlerprüfblock sehen.

Aber Sie können dieses Argument für jeden Funktionsaufruf verwenden! Wenn ich strings.HasPrefix("foobar", "foo") aufrufe, wird dann der Code verschleiert? Möchten Sie lieber l := len("foo"); len("foobar") >= l && s[0:l] == "foo" schreiben und lesen?

@rossmcf

Jede Fehlerprüfung ist explizit.

try überprüft den Fehler immer noch explizit. Es ist die Daseinsberechtigung des Versuchs.

Ich würde auch denken, dass wenn err != nil Überprüfungen mühsam erscheinen können, ich sie nicht wirklich eingeben muss.

Es ist nicht mühsam zu tippen. Es ist mühsam zu lesen, wenn wir überall die gleichen 3 Zeilen haben. Es ist eine Wiederholung, und wir als Programmierer neigen normalerweise dazu, diese auszuklammern. Vielleicht hat try andere Nachteile, aber nicht diesen, denke ich.

try überprüft den Fehler immer noch explizit
Der Unterschied zwischen der Abstraktion try und strings.HasPrefix besteht darin, dass try implizit zurückgibt.
Beim Lesen von Go-Code weiß ich, dass der Ausführungsfluss innerhalb meiner Funktion bleibt, bis ich:

  • die schließende Klammer einer Funktion ohne Rückgabetypen lesen
  • Lesen Sie die Schlüsselwörter return , panic
  • lesen syscall.Exit(code)
    Mit try konnte ich Code nicht auf die gleiche Weise lesen. Visuell über Zeilen zu scannen und null "return"-Anweisungen zu sehen, würde nicht mehr bedeuten, dass "entweder diese Zeilen alle ausgeführt werden oder einer blockiert oder das Programm beendet wird".

@ngrilly Sie können mehr als einer Person in einem Beitrag zur Information antworten, 10 Antworten in ein paar Stunden mit 5 hintereinander an einer Stelle machen es schwierig, der Diskussion zu folgen. Nachdem ich Ihre Beiträge, abgesehen von einigen Irrtümern, gelesen habe, habe ich keine neuen konkreten Argumente gesehen, die die Vorteile von Try beschreiben. Ich habe einen einzigen Vorteil gesehen: das Verhindern der Eingabe von if err != nil . Dies geht zu Lasten der Einführung neuer Wege zum Verlust von Ressourcen , der Möglichkeit, weniger prägnanten Code zu schreiben, und das Schlimmste, die Verschachtelung von Try ist möglich .

Ich halte die Spezifikation und die Argumente der Befürworter für irreführend, sie liefert derzeit nur die besten Beispiele, ohne die schlimmsten Beispiele zu zeigen. Es werden weder die oben genannten negativen Nachteile noch mögliche mildernde Faktoren bewertet oder erwähnt. Es begründet nicht, warum es die Implementierung von Try nicht auf die vorgeschlagene und nachgewiesene Verwendung beschränkt. Das Tool tryhard wird verwendet, um kompakteren Code anzuzeigen, der manchen Leuten subjektiv eine bessere Ästhetik bietet, ohne ein trytoohard Tool, das zeigt, was es alles kann, z. B. tief verschachtelte Versuche Aussagen. Schließlich wird der Name selbst in der Programmierwelt allgegenwärtig mit Ausnahmen in Verbindung gebracht, ermöglicht es ihm, verschachtelt und fehlerbezogen zu sein, und platziert ihn als eingebautes Element neben einer nicht verwandten Wiederherstellung und Panik, sodass die Dinge einfach fehl am Platz aussehen. Ich glaube und vertraue auf die Fähigkeit der Go-Autoren, sich etwas Besseres einfallen zu lassen.

Es gibt zu viele Kosten hier, um sie mit einem einzigen Mehrwert zu rechtfertigen, den ich in den Antworten der Befürworter hochgewürgt sehe: "Ich muss nicht mehr if err != nil eingeben" - die Sache, die zusammen mit den Fehlern leidenschaftlich verteidigt und verwurzelt wurde, sind Werte der gesamten Go-Community. Wir haben bis zu einem Jahrzehnt Code geschrieben, der mit if err != nil - was einige der bemerkenswertesten technologischen Fortschritte (Docker, k8s, ...) alle gleichzeitig mit großem Erfolg verwenden.

Zusammenfassend lässt sich sagen, dass if err != nil keine Last ist, sich mit einem Built-In zu verstecken, sondern etwas, das wir alle als Kernbestandteil des Sprachenerfolgs anerkennen sollten. Auch wenn wir kollektiv akzeptieren, dass es eine Belastung ist, sollte die Messlatte dafür hoch und kompromisslos hoch sein. Im Moment sind zu viele Aspekte des Versuchs ein Kompromiss.

Ich habe Meinungen darüber, welche Methode einfacher ist, aber es sind Meinungen. Gegebener Versuch ist einfach und die aktuellen expliziten Prüfungen sind einfach. Großartig, beide Wege sind einfach. Das Problem für mich ist, dass es die kognitive Belastung sowohl des Lesers als auch des Schreibers eines bestimmten Codes erhöht. Jetzt müssen beide verschiedene Möglichkeiten interpretieren, um Dinge zu tun. Und der Autor muss entscheiden, wie er die Dinge tun möchte und/oder riskiert, Dinge anders zu machen als der Rest des Pakets oder Projekts, an dem er arbeitet. Wenn try die explizite Prüfung ersetzen würde, würde dies die kognitive Belastung aufgrund impliziter Rückgaben als eine weitere zu analysierende Sache immer noch erhöhen.

_Wenn wir all das für einen Moment beiseite legen und bedenken, dass wir jetzt zwei gleich einfache Möglichkeiten haben, mit Fehlern umzugehen, haben wir immer noch ein Problem:_ Einfach ist nicht mehr einfach . Und das öffnet die Tür zu all den Dingen, die man vermeiden sollte.

Die Latte, so etwas hinzuzufügen, sollte viel höher liegen, sollte noch lange experimentell sein, um zu beweisen, dass es mit Daten aus der Praxis besser ist.

@cstockton

Sie können in einem Beitrag mehr als einer Person antworten Zur Info, 10 Antworten in ein paar Stunden und 5 hintereinander machen es schwierig, der Diskussion zu folgen.

Ian hat vor 7 Tagen

@therealplato

Der Unterschied zwischen der Try-Abstraktion und strings.HasPrefix besteht darin, dass try implizit zurückgibt.

Das stimmt. Wenn wir eine Funktion lesen und nach "Exit"-Punkten suchen, müssen wir nach return, panic, log.Panic, os.Exit, log.Fatal usw. suchen und es versuchen. Ist das so ein Problem? Die Anzahl der Austrittspunkte in einer Funktion bleibt gleich und wird trotzdem explizit markiert, mit oder ohne Try.

Lesen Sie die Schlüsselwörter Rückkehr, Panik

Panik ist kein Stichwort; Es ist eine eingebaute Funktion. Wenn wir den Vorschlag des Go-Teams kritisieren, das in Bezug auf Sprachdesign wahrscheinlich kompetenter ist als jeder von uns, dann sollten wir ihnen zumindest den Gefallen tun, die Dinge richtig zu definieren. 😉

Wenn wir eine Funktion lesen und nach "Exit"-Punkten suchen, müssen wir nach return, panic, log.Panic, os.Exit, log.Fatal usw. suchen und es versuchen. Ist das so ein Problem?

Dies ist ein Problem, da try buchstäblich überall dort erscheinen kann, wo ein Ausdruck vorkommen kann. Jeder einzelne Ausstiegspunkt in Go kann _nur_ als einzelne Anweisung auftreten, try ist das einzige, was als Ausdruck erscheinen kann.

@ngrilly

Ian hat vor 7 Tagen vorgeschlagen, diese Diskussion genau aus diesem Grund auf Golang-Nuts zu verschieben (keine Möglichkeit, auf einen bestimmten Kommentar zu antworten, kein Threading), Vorschlag, der abgelehnt wurde, um sicherzustellen, dass die Diskussion "offiziell" ist. Wir haben, wonach wir gefragt haben.

Nachricht beginnen

@ user1
Antwort 1

@ user2
antworten 2

Nachricht beenden

Das war gemeint.

@cstockton

Ich habe einen einzigen Vorteil gesehen: das Verhindern der Eingabe von if err != nil.

try verhindert das wiederholte Tippen und Lesen von if err != nil { return ..., err } (auf 3 Zeilen formatiert), nicht nur if err != nil .

Dies geht zu Lasten der Einführung neuer Möglichkeiten, um Ressourcen zu verlieren, der Möglichkeit, weniger prägnanten Code zu schreiben, und das Schlimmste erlaubt die Verschachtelung von Try.

Das von Ihnen erwähnte Risiko von Ressourcenlecks kann mit vet und lint .

Über "weniger prägnanter Code" besteht der Sinn von try darin, prägnanteren Code zu schreiben, daher verstehe ich Ihr Argument nicht.

Das Risiko einer übermäßigen Verschachtelung von Funktionsaufrufen ist nicht spezifisch für try . Alle Funktionsaufrufe können übermäßig verschachtelt sein. Code-Reviews und Linting helfen wie immer.

Ich halte die Spezifikation und die Argumente der Befürworter für irreführend, sie liefert derzeit nur die besten Beispiele, ohne die schlimmsten Beispiele zu zeigen.

Vielleicht beruht dieses Gefühl auf Gegenseitigkeit. Wir sind alle schöne Gophers; fallen wir nicht in Werturteile ;-)

Ich sehe in Antworten von Befürwortern hochgewürgt: "Ich muss nicht mehr tippen, wenn err != nil"

Auch hier muss ich nicht mehr l := len("foo"); len("foobar") >= l && s[0:l] == "foo" .
Ich kann stattdessen strings.HasPrefix("foobar", "foo") verwenden.
Wie ist das bei try so anders?
Ich habe vorhin gelesen, dass Sie ein "eingeschränktes" try akzeptieren würden, das den Namen check und das Verschachteln verbietet.

Wir haben bis zu einem Jahrzehnt Code geschrieben, der mit if err != nil geschrieben wurde - was einige der bemerkenswertesten technologischen Fortschritte (Docker, k8s, ...) alle gleichzeitig mit großem Erfolg verwenden.

Wir haben auch eine Menge großartigen Code in C, C++, Java usw. geschrieben. Mit dieser Argumentation hätten wir Go nicht.

Als ich die Diskussionen über die Fehlerbehandlung in Go las, hatte ich nicht das Gefühl, dass alle auf der gleichen Seite bezüglich des try Vorschlags waren, also beschloss ich, einen Blogbeitrag zu schreiben, der zeigt, wie try kann verwendet werden: https://faiface.github.io/post/how-to-use-try/

Zugehörige Diskussion auf Reddit: https://www.reddit.com/r/golang/comments/c9eo3g/how_to_use_try_faiface_blog/

Ich weiß, dass dieses Problem gegen try , aber ich hoffe, mein Beitrag kann einige neue Perspektiven bringen :)

Go steckt fest und kämpft zwischen magisch oder logisch für die Idee einfach.

Vorteile:

  • Boilerplate reduzieren
  • Einfach
  • Vorhandenes Muster unter anderen Sprachen
  • Optional

Nachteile:

  • Lernkurve
  • Grundlegende Magie
  • Neue Arten von Fehlern
  • Go ist eigensinnig und pragmatisch mit if != nil aber Sie könnten try

Ich fühle mich wie diese Gemeinschaft insb. unterscheidet sich hier von Personen, die in der Go-Umfrage [1] gewählt wurden .
Die Wähler könnten dies nicht als Hauptanliegen wählen, sondern für zukünftige Überlegungen belassen.
Es wurde jedoch aufgrund seiner Platzierung als einflussreich angesehen.

IMO, das Hinzufügen von Sprachfunktionen ist alt und die moderne Programmiermethode fügt den Editoren weitere Funktionen hinzu, z , UI über dem Quellcode und lassen Sie den Quellcode ausführlich
Code if err != nil in ein try falten

@ngrilly

try verhindert das wiederholte Tippen und Lesen von if err != nil { return ..., err } (auf 3 Zeilen formatiert), nicht nur if err != nil.

Glauben Sie mir, dass ich mit der Aussage "es hindert Sie daran zu tippen if err != nil" völlig vergessen hatte, dass wir auch den Code lesen, den wir tippen?

Das von Ihnen erwähnte Risiko von Ressourcenlecks kann mit Tierarzt und Flusen verhindert werden.

Ich habe eine Diskussion verlinkt, warum ich der Meinung bin, dass Tierarzt und Flusen hier keine vernünftige Option sind.

Bei "weniger prägnantem Code" besteht der einzige Versuch darin, prägnanteren Code zu schreiben, daher verstehe ich Ihr Argument nicht.

Ja, hätten Sie den Link gelesen, auf den "die Fähigkeit , weniger prägnanten Code zu

Das Risiko einer übermäßigen Verschachtelung von Funktionsaufrufen ist nicht spezifisch. Alle Funktionsaufrufe können übermäßig verschachtelt sein. Code-Reviews und Linting helfen wie immer.

Interessant, lass uns das mal aufschlüsseln:

1) Das Risiko einer übermäßigen Verschachtelung von Funktionsaufrufen ist nicht spezifisch zu versuchen.

Ja, jeder hier versteht, wie Funktionen funktionieren.

2) Alle Funktionsaufrufe können übermäßig verschachtelt sein.

Ja, jeder hier versteht, wie Funktionen funktionieren.

3) Code-Reviews und Linting helfen wie immer.

Ja, Sie haben die Flusen Argument bereits und das „Code - Reviews Argument“ ist ein weitere außerhalb Sprachsteuerung , die in dem Pfosten gemacht wurde ich verbunden dir .

Vielleicht beruht dieses Gefühl auf Gegenseitigkeit. Wir sind alle schöne Gophers; fallen wir nicht in Werturteile ;-)

Ich verstehe nicht? Der Vorschlag enthält keine Beispiele für die volle Leistungsfähigkeit der Implementierung, sondern nur die beabsichtigte Verwendung. Das tryhard Tool, das verwendet wird, um die Auswirkungen des Angebots zu messen, verwendet try in der begrenztesten und vernünftigsten Form. Dies ist eine einfache Tatsache.

Auch hier muss ich nicht mehr l := len("foo"); len("foobar") >= l && s[0:l] == "foo".
Ich kann stattdessen strings.HasPrefix("foobar", "foo") verwenden.
Wie ist das so anders beim Versuch?

Ich tue mein Bestes, um aus jeder gegensätzlichen Sichtweise eine Position zu extrahieren, sonst kann ich keine Argumente bilden, um sie zu demontieren. Das kann ich hier wirklich nicht sehen, tut mir leid. Ich werde das so interpretieren, wie ich es verstehen kann: wörtlich.

Wie ist es ( strings.HasPrefix ) so anders mit try ?

strings.HasPrefix

func HasPrefix

func HasPrefix(s, Präfix-String) bool

HasPrefix testet, ob die Zeichenfolge s mit Präfix beginnt.

Versuchen

func try ist ein neues funktionsähnliches Built-In namens try mit Signatur (Pseudo-Code)

func try(expr) (T1, T2, … Tn)

Dabei steht expr für einen eingehenden Argumentausdruck (normalerweise ein Funktionsaufruf), der n+1 Ergebniswerte der Typen T1, T2, ... Tn und Fehler für den letzten Wert erzeugt. Wenn expr einen einzelnen Wert ergibt (n ist 0), muss dieser Wert vom Typ error sein und try gibt kein Ergebnis zurück. Der Aufruf von try mit einem Ausdruck, der keinen letzten Wert vom Typ error erzeugt, führt zu einem Kompilierzeitfehler.

Das eingebaute try darf nur innerhalb einer Funktion mit mindestens einem Ergebnisparameter verwendet werden, bei der das letzte Ergebnis vom Typ error ist. Der Aufruf von try in einem anderen Kontext führt zu einem Kompilierzeitfehler.

Aufruf von try mit einem Funktionsaufruf f() wie in (Pseudo-Code)

x1, x2, … xn = try(f())

turns into the following (in-lined) code:

t1, … tn, te := f()  // t1, … tn, te are local (invisible) temporaries
if te != nil {
        err = te     // assign te to the error result parameter
        return       // return from enclosing function
}
x1, … xn = t1, … tn  // assignment only if there was no error

Mit anderen Worten, wenn der letzte von "expr" erzeugte Wert vom Typ error nil ist, gibt try einfach die ersten n Werte zurück, wobei der letzte nil-Fehler entfernt wird. Wenn der letzte von "expr" erzeugte Wert nicht nil ist, wird die Fehlerergebnisvariable der einschließenden Funktion (die im obigen Pseudocode als err bezeichnet wird, aber einen anderen Namen haben oder unbenannt sein kann) auf diesen Fehlerwert ungleich null gesetzt und die einschließende Funktion kehrt zurück. Wenn die einschließende Funktion andere benannte Ergebnisparameter deklariert, behalten diese Ergebnisparameter ihren Wert bei. Wenn die Funktion andere unbenannte Ergebnisparameter deklariert, nehmen sie ihre entsprechenden Nullwerte an (was dem Beibehalten des bereits vorhandenen Werts entspricht).

Wenn try in einer Mehrfachzuweisung wie in dieser Abbildung verwendet wird und ein Fehler ungleich Null erkannt wird, wird die Zuweisung (zu den benutzerdefinierten Variablen) nicht ausgeführt und keine der Variablen auf der linken Seite des Zuordnung geändert werden. Das heißt, try verhält sich wie ein Funktionsaufruf: Seine Ergebnisse sind nur verfügbar, wenn try zur tatsächlichen Aufrufstelle zurückkehrt (im Gegensatz zur Rückkehr von der einschließenden Funktion). Wenn die Variablen auf der linken Seite Ergebnisparameter sind, führt die Verwendung von try zu einem anderen Ergebnis als der heute übliche Code. Wenn beispielsweise a, b und err alle benannte Ergebnisparameter der einschließenden Funktion sind, ist dieser Code

a, b, err = f()
if err != nil {
        return
}

setzt immer a, b und err, unabhängig davon, ob f() einen Fehler zurückgegeben hat oder nicht. Im Gegensatz

a, b = try(f())

lässt a und b im Fehlerfall unverändert. Obwohl dies ein subtiler Unterschied ist, glauben wir, dass Fälle wie diese selten sind. Wenn ein aktuelles Verhalten erwartet wird, behalten Sie die if-Anweisung bei.

Sie unterscheiden sich darin, dass nicht der gesamte Text in der Beschreibung von try in strings.HasPrefix enthalten ist. Eine bessere Frage wäre, wie ähnlich sind, auf die ich antworten würde, dass beide einige Aspekte von Anrufen teilen und sonst nichts.

Ich habe vorhin gelesen, dass Sie einen "eingeschränkten" Versuch akzeptieren würden, der als check bezeichnet würde und das Verschachteln verbieten würde.

Schön, dass Sie mein zentrales Argument gegen try gelesen haben: Die Implementierung ist nicht restriktiv genug. Ich glaube, dass entweder die Implementierung allen Vorschlägen entsprechen sollte, die Anwendungsbeispiele prägnant und leicht zu lesen sind.

_Oder_ der Vorschlag sollte Beispiele enthalten, die der Implementierung entsprechen, damit alle Personen, die dies in Betracht ziehen, mit dem konfrontiert werden können, was unweigerlich im Go-Code erscheinen wird. Zusammen mit all den Eckfällen, mit denen wir bei der Fehlerbehebung von weniger als ideal geschriebener Software konfrontiert sein können, die in jeder Sprache / Umgebung auftritt. Es sollte Fragen beantworten wie, wie Stacktraces mit mehreren Verschachtelungsebenen aussehen werden, sind die Orte der Fehler leicht erkennbar? Was ist mit Methodenwerten, anonymen Funktionsliteralen? Welche Art von Stack-Trace wird erzeugt, wenn die Zeile mit den Aufrufen von fn() fehlschlägt?

fn := func(n int) (int, error) { ... }
return try(func() (int, error) { 
    mu.Lock()
    defer mu.Unlock()
    return try(try(fn(111111)) + try(fn(101010)) + try(func() (int, error) {
       // yea...
    })(2))
}(try(fn(1)))

Ich bin mir bewusst, dass viel vernünftiger Code geschrieben werden wird, aber wir bieten jetzt ein Werkzeug an, das es noch nie gegeben hat: die Möglichkeit, Code möglicherweise ohne klaren Kontrollfluss zu schreiben. Ich möchte also begründen, warum wir es überhaupt zulassen. Ich möchte nie meine Zeit damit verschwenden, diese Art von Code zu debuggen. Weil ich weiß, dass ich es tun werde, hat mich die Erfahrung gelehrt, dass jemand es tun wird, wenn Sie es zulässt. Dass jemand oft ein uninformiertes Ich ist.

Go bietet anderen Entwicklern und mir die geringstmöglichen Möglichkeiten, uns gegenseitig Zeit zu verschwenden, indem wir uns darauf beschränken, dieselben banalen Konstrukte zu verwenden. Ich möchte das nicht ohne einen überwältigenden Vorteil verlieren. Ich glaube nicht, dass "weil Try als Funktion implementiert ist" ein überwältigender Vorteil ist. Können Sie einen Grund angeben, warum das so ist?

Verschwenden Sie keine Zeit mit diesem Fehlerbehandlungsproblem, geben Sie uns Generika und wir werden so etwas wie Rusts Ergebnis erstellen.

Go steckt fest und kämpft zwischen magisch oder logisch für die Idee einfach.

Vorteile:

  • Boilerplate reduzieren
  • Einfach
  • Vorhandenes Muster unter anderen Sprachen
  • Optional

Nachteile:

  • Lernkurve
  • Grundlegende Magie
  • Neue Arten von Fehlern
  • Go ist eigensinnig und pragmatisch mit if != nil aber Sie könnten try

Ich fühle mich wie diese Gemeinschaft insb. unterscheidet sich hier von Personen, die in der Go-Umfrage gewählt wurden [1] .
Die Wähler könnten dies nicht als Hauptanliegen wählen, sondern für zukünftige Überlegungen belassen.
Es wurde jedoch aufgrund seiner Platzierung als einflussreich angesehen.

IMO, das Hinzufügen von Sprachfunktionen ist alt und die moderne Programmiermethode fügt den Editoren weitere Funktionen hinzu, z , UI über dem Quellcode und lassen Sie den Quellcode ausführlich
Code if err != nil in ein try falten

Ich war einer, der für eine strengere Fehlerbehandlung gestimmt hat, ohne die Möglichkeit zu haben, die Verarbeitung eines Fehlers zu vergessen. Nicht zum Probieren.

Wir brauchen viel mehr als Generika, um etwas aus der Ferne wie den Result Typ von Rust neu zu Result nur mit Generika erstellt werden könnte, müssten Programmieranfänger dann Generika kennen, bevor sie überhaupt einen Fehler richtig behandeln oder einen Fehler von einer Funktion "auf die Art von Result können "

@deanveloper , mein Punkt ist: Ich habe viel mehr von Generika als von "Syntaxänderung" und ich glaube, das gilt auch für die Community.

@txgruppi Ich kann zustimmen, dass Generika eine höhere Priorität haben sollten. Ich wollte nur sagen, dass ich nicht glaube, dass Generika ein guter Ersatz für die Fehlerbehandlung sein werden.

@deanveloper , meiner Meinung nach ist dieses Fehlerbehandlungsproblem nur Kosmetik, die Leute verbringen Zeit damit, etwas darüber zu diskutieren, dass es stabil ist und gut funktioniert, nur weil Sie ein paar zusätzlichen Code eingeben müssen. Erfahren Sie einfach, wie Sie besseren Code schreiben und dies mit Designänderungen beheben.
Bevor jemand sagt, dass Generika genauso einfach mit besserem Code zu beheben sind: Fehler bei der Kompilierung ...

kann es mit einem Snippet oder einem Tastaturmakro gelöst werden? dann ist es kein Thema.

@txgruppi

Erfahren Sie einfach, wie Sie besseren Code schreiben und dies mit Designänderungen beheben.

70 % des gesamten Fehlerbehandlungscodes in der Standardbibliothek sind derzeit für try geeignet, wie Robert Griesemer mit seinem Werkzeug tryhard . Mit Änderungen am Code, wie der (noch nicht vorhandenen) Funktion fmt.HandleErrorf wären mehr möglich. Ich hoffe, Sie wollen die Standardbibliothek nicht als schlechten Code bezeichnen.

kann es mit einem Snippet oder einem Tastaturmakro gelöst werden? dann ist es kein Thema.

Es geht auch darum, den Code zu lesen. Deshalb mögen wir nicht thing.Thing thing = new thing.Thing(thing.THING);

@faiface , kommt 'if err != nil' auf den Weg, qualitativ hochwertige Software zu entwickeln? Ich glaube nicht.
Kommt der Mangel an Generika auf den Weg, qualitativ hochwertige Software zu entwickeln? Ja, so ist es.

Ich sehe es so: Ich habe nicht genug Wissen, um Generika zu implementieren, also brauche ich jemanden, der sie implementiert, aber diese Fehlerbehandlung ist nur Zeitverschwendung für diejenigen, die Generika Wirklichkeit werden lassen können. Ich bin nicht gegen diese Fehlerbehandlung, weil sie eine schlechte Sache ist, ich bin dagegen, weil es wichtigere Dinge zu lösen gibt.

@faiface Die Standardbibliothek ist keine gute Darstellung von echtem Go-Code. Dies ist , weil es viel wahrscheinlicher , dass die Standard - Bibliothek ist einfach Pass-up - Fehler ohne Kontext hinzufügen, zum Beispiel io/ioutil muss nie wirklich Fehler dekorieren, kann er einfach den Fehler passieren , die in aufgetreten io . Robert Griesemer gab auch zu, dass die stdlib nicht gerade der beste Vertreter des echten Go-Codes ist, aber ich bin gerade auf dem Handy und möchte nicht nach dem Kommentar suchen. Ich bin mir ziemlich sicher, dass es seinem ursprünglichen Tryhard-Post relativ nahe kam.

@deanveloper @faiface Wenn Sie gegen den Go Corpus antreten :

--- stats ---
 401679 (100.0% of  401679) func declarations
  97496 ( 24.3% of  401679) func declarations returning an error
 991348 (100.0% of  991348) statements
 217490 ( 21.9% of  991348) if statements
  88891 ( 40.9% of  217490) if <err> != nil statements
    485 (  0.5% of   88891) <err> name is different from "err" (-l flag lists details)
  59500 ( 66.9% of   88891) return ..., <err> blocks in if <err> != nil statements
  29391 ( 33.1% of   88891) complex error handler in if <err> != nil statements; cannot use try (-l flag lists details)
    596 (  0.7% of   88891) non-empty else blocks in if <err> != nil statements; cannot use try (-l flag lists details)
  52810 ( 59.4% of   88891) try candidates (-l flag lists details)

Im realen Code werden also 40% der if-Anweisungen zur Fehlerprüfung geschrieben, und try kann 59% von ihnen sofort eliminieren.

Ich stimme zu. Mir geht es gut mit if err != nil. Es ist einfach und sauber für Funktionen, die einzelne Fehlerwerte zurückgeben. Ich mag auch das Fehlerpaket und seine Ursachen- / Wrap-Funktionen, wenn der Kontext des Fehlers von Bedeutung ist. Wenn Sie benutzerdefinierte Fehler mit einer Codeeigenschaft verwenden (soweit ich weiß), müssen Sie entweder eine Typzusicherung durchführen oder etwas anstelle der Standardfehlerschnittstelle verwenden. Wie auch immer, ich habe noch nie Go-Code gelesen oder geschrieben und mich darüber geärgert, wie die Fehlerbehandlung derzeit funktioniert. Die Ärgernisse, auf die ich gestoßen bin, sind Fälle, in denen mehrere Fehler als Ergebnis der Verarbeitung einer Sammlung von Elementen auftreten können. Dies ist jedoch ein Designproblem und kein Sprachproblem.

Beachten Sie, dass, wenn ich sage "wenn der Kontext des Fehlers wichtig ist", ich mich auf eine Situation beziehe, in der möglicherweise ein Netzwerk-Hiccup aufgetreten ist und ich es daher erneut versuchen möchte, oder die Ergebnisse eines Aufrufs vom Typ "find" keine Ergebnisse zurückgegeben haben, weil es gab eigentlich keine, oder mein zuckender Finger hat meiner SQL-Abfrage ein zufälliges "s" hinzugefügt, was sie explodieren lässt (das passiert mir oft ... ich sollte wahrscheinlich auf Nervenschäden untersucht werden).

Am 05.07.19 schrieb Nicolas Grilly [email protected] :

@kroppt Ich habe Dutzende Male gelesen
den Code verschleiern. try ist nur eine eingebaute Funktion, die verwendet wird, um einige zu faktorisieren
sich wiederholender Code. Das machen wir als Programmierer die ganze Zeit. Wenn wir a . identifizieren
sich wiederholendes Muster, wir berücksichtigen es in einer neuen Funktion. Wenn nicht, wir
hätte eine lange main()-Funktion, in die unser gesamter Code eingebettet ist.

Ich bin versucht, Sie unaufrichtig zu nennen, aber ich respektiere Ian Lance Taylors
übermenschliche Bemühungen, die Diskussion höflich zu halten und ich kann es wirklich nicht
Mal sehen, was jemand gewinnen würde, wenn er absichtlich in diesem Forum lügt.

Das heißt: "Wenn wir ein sich wiederholendes Muster identifizieren, rechnen wir es aus
eine neue Funktion." Klar, aber nicht durch die Bereitstellung eines widersprüchlichen Konstrukts
das in dieser späten Phase der Entwicklung von Go lange mit zwei kommt
versteckte Funktionen: Die erste behandelt Funktionen, deren "Rückkehr"
Argumentliste endet in einem error Wert" als Special (oder alles andere
als semantischer Fehler) und der zweite bietet einen versteckten Kontrollfluss
Umweg, der analog, aber nicht ganz identisch mit einer "Rückkehr" ist
Stellungnahme.

Kümmern Sie sich nicht um die flammenden Reifen, die die Verwendung von "Aufschub" zum Dealen einführt
mit geheimnisvolleren Verwendungen von "versuchen - die Pseudofunktion". Jemand
an anderer Stelle sagte ungefähr "Ich möchte nicht auf try im Code stoßen
Ich lese". Mir geht es genauso und das sollte nicht unter den Tisch gekehrt werden
Teppich.

Ich habe erklärt, dass es der "Rückgabe"-Aspekt von "Fehlerrückgabe" ist, der
behandelt werden muss, und der Vorschlag "on err" kommt dem am nächsten
dieses Prinzip, verbiegt aber auch die Regeln etwas. Mein eigenes auch
"Fail"-Vorschlag (es verschiebt das letzte Argument zum ersten, das ist
macht mich unglücklich).

Tiefer, was keine Sprache mit der möglichen Ausnahme von SNOBOL,
mit dem ich vertraut bin, hat den Sprung gewagt, den Rob Pike als
"Fehler sind Werte" in dem Maße wie Go, aber etwas ist verloren gegangen
dabei: ein Fehler "Bedingung" ist "kein" Wert. Erfolgreich
Die Vervollständigung einer Funktion ist ein Sonderfall und somit auch jede möglich
Versagen.

Jeder (und das gilt für den erfolgreichen Abschluss, von denen es sein kann
mehr als eine) muss nach ihrem Verdienst behandelt werden, aber wir
bestehen darauf, dass die aufgerufene Funktion uns ihre Meinung zu mitteilen muss
die Qualität der Fertigstellung in abgekürzter Form, etwas, das
für immer getan und Rob hat sich als Irrtum erwiesen.

Betrachten Sie zur Veranschaulichung die Rückgabewerte eines Readers:
io.EOF ist ein Sonderfall, der manchmal erfolgreich und manchmal a
Fehler, aber nach Go-Standards ist er stolz ein Fehler ("io.Err != nil").
Können wir das auch irgendwie abkürzen? Fast sicher
nicht, weil wir es gewohnt sind, seine "Unrichtigkeit" zu "verzeihen".

Ich habe lange auf einen Loop-Exit gewartet, um einen ähnlichen "Status" zu vermitteln oder
Bedingungscode (eine Suche kann mit einem gefundenen Wert oder einem Fehler enden, wie
sagst du was, wenn du verschiedene dinge machen möchtest? Sie fügen einen Test hinzu,
wo das Wissen bereits vorhanden ist - gleiches Thema, anders
Kontext).

Dies sind echte Verbesserungen gegenüber traditionellen Sprachen: Reduzierung des Kessels
Platte ist im Vergleich absurd.

Ebenso gültig ist der Vergleich mit dem ternären Operator: if ?:
ist nicht erlaubt "damit es nicht missbraucht wird", dann darf versuchen nicht erlaubt sein,
entweder, zumindest aus diesen Gründen.

Ehrlich gesagt, versuchen ist eine Lerche. Es macht Go für die Falschen attraktiver
Publikum. Abgesehen von der Gefahr dabei - wer will schon die falschen Leute
unserer Gemeinschaft beitreten? - es gibt die Frage des Präzedenzfalls und die Frage
von unbeabsichtigten Folgen. Ich würde sagen "verschrotte es" und lass uns das akzeptieren
Wir sind noch nicht an einem Punkt angekommen, an dem Abwärtskompatibilität möglich ist
missachtet werden, daher muss die Fehlerbehandlung ausführlich bleiben.

Wir haben Panik/Erholung und es muss von drittklassig gefördert werden
Bürger zum willigen Helfer kann es sein, mit allen Erklärungen
das macht Neulinge (und ich, ich gebe zu, davor Angst zu haben) mehr
zuversichtlich, es zu verwenden.

Das Konstrukt "defer" innerhalb der Fehlerbehandlung (das ich übernommen habe -
ohne ihre Bedeutung an anderer Stelle zu erkennen - konsequent in der Finalisierung
SQL-Transaktionen: tx.Rollback/tx.Commit) war für mich eine Offenbarung.
Möglicherweise gibt es weitere Prinzipien, die "im Rahmen" von gelernt werden können
Was Go bereits bietet: Bleiben wir vorerst in dieser Box.

Eine solche Sache aus dem Stegreif wäre, zu einer Fehlerberichterstattung überzugehen
Funktion eine Liste von "error.Methods" die unter konventionellen
Bedingungen (io.EOF, sql.ErrNoRows), anstatt das Ergebnis zu melden
als schwarz-weiß. Aber ich bin in solchen Dingen ungeschult, meine Vorschläge
sind zu naiv, lass andere (Roger, hörst du zu?) ähnlich pflegen
Ideen zu verwirklichen.

Lucio.

"Es ist nicht mühsam zu tippen. Es ist mühsam zu lesen, wenn wir dasselbe haben
3 Zeilen überall. Es ist eine Wiederholung, und wir als Programmierer normalerweise
neigen dazu, diese auszuklammern. Vielleicht hat der Versuch andere Nachteile, aber das nicht
eine, denke ich."

Wieder unaufrichtig oder zumindest rationalisierend. Ich gebe zu, dass die
drei Zeilen werden viel öfter gelesen als geschrieben, aber die
Schmerzen, die Griesemer lindern soll, liegt im Schreiben nicht im Lesen

  • was er zweifellos als eine vorteilhafte Konsequenz wahrnimmt.

Aber "err != nil" ist ein sehr bekannter Marker und beim "Lesen" - als
Im Gegensatz zum "Suchen" mit einem Editor ist dieses Muster sowohl einfach zu
vor Ort und einfach zu stornieren. Ausklammern ist nicht dasselbe
Liga überhaupt. Und der Preis stimmt nicht.

"Wir als Programmierer" neigen dazu, komplexere Muster zuerst auszuklammern,
auch wenn sie selten vorkommen. Wir wissen auch, dass "if err != nil { return
err }" wird zu einer sehr einfachen Befehlsfolge kompiliert, wenn
Wer das nicht tut, soll hier die Hand heben. Kann man gleich sein
zuversichtlich, dass es mit "versuchen - die Funktion" passieren wird?

Lucio.

@lootch Hey Mann, es ist wirklich unproduktiv, Leute als unaufrichtig zu bezeichnen, ich bin mir ziemlich sicher, dass sie es nicht sind, da die Aussagen, die Sie als solche gekennzeichnet haben, ziemlich vernünftig sind.

Wir rechnen als Programmierer sich wiederholende Muster aus, das ist absolut richtig.

Viele sich wiederholende Codes verlangsamen das Lesen, daher sehe ich auch nicht, dass das unaufrichtig ist.

Ihre Gegenargumente sind im Grunde "Komm schon, Mann, das ist keine so große Sache". Nun, für viele ist es das.

Wer will, dass die falschen Leute unserer Community beitreten?

Das ist super arrogantes Gate Keeping. Außerdem hat das tryhard Tool gezeigt, dass try in vielen der heutigen Go-Codebasen direkt anwendbar ist. Es kann direkt auf 70% des Fehlerbehandlungscodes in der Standardbibliothek angewendet werden. Mit Änderungen am Code (um Fehlerdekoration mit defer usw.) glaube ich, dass dies auf mehr als 80% der Fehlerbehandlung im gesamten Go-Code anwendbar ist.

Zugegeben, ich überschreite hier das Ziel. Ich entschuldige mich.

Ich schätze, einige von uns werden bei dieser Diskussion heiß unter den Kragen
dreht sich im Kreis.

Am 07.07.19 schrieb Michal Štrba [email protected] :

@lootch Hey Mann, es ist wirklich unproduktiv, Leute als unaufrichtig zu bezeichnen, ich bin
ziemlich sicher, dass sie nicht so sind, wie die Aussagen, die Sie als solche markiert haben, hübsch sind
vernünftig.

Wir rechnen als Programmierer sich wiederholende Muster aus, das ist absolut
wahr.

Viel sich wiederholender Code verlangsamt das Lesen, also verstehe ich nicht, wie das ist
auch unaufrichtig.

Ihre Gegenargumente sind im Grunde "Komm schon, Mann, so ist es nicht"
große Sache". Nun, für viele Leute ist es das.

Wer will, dass die falschen Leute unserer Community beitreten?

Das ist super arrogantes Gate Keeping. Außerdem hat das tryhard Tool gezeigt, dass
try ist in vielen der heutigen Go-Codebasen direkt anwendbar. Es kann sein
direkt auf 70 % des Fehlerbehandlungscodes in der Standardbibliothek angewendet. Mit
Änderungen am Code (um Fehlerdekoration mit defer usw.), glaube ich
es wird auf mehr als 80 % der Fehlerbehandlung im gesamten Go-Code anwendbar sein.

--
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an:
https://github.com/golang/go/issues/32825#issuecomment -508971768

--
Lucio De Re
2 Piet Retief Straße
Kestell (Ost-Freistaat)
9860 Südafrika

Tel.: +27 58 653 1433
Handy: +27 83 251 5824
FAX: +27 58 653 1435

@lootch Requisiten zum Selbstbewusstsein! Ich kann die Frustration verstehen, wenn sich die Diskussion im Kreis dreht.

Ich sehe das auch ähnlich und bin auf der anderen Seite.

Vielleicht verstehen sich beide Seiten einfach nicht. Haben Sie meinen Blog-Beitrag mit dem Titel Wie benutzt man 'try' gelesen

Am 07.07.19 schrieb Michal Štrba [email protected] :

[ ... ]
Vielleicht verstehen beide Seiten die andere Seite einfach nicht. Hast du
Lesen Sie meinen Blog-Beitrag namens How to use 'Versuchen'? wo ich es versuche
Zeigen Sie, wie die Verwendung von 'try' in der Praxis aussehen würde, und geben Sie mein Bestes, um zu bleiben
unvoreingenommen?

Ich gestehe, ich habe es nicht getan, ich wünsche mir inständig, dass ich es nie muss :-)

Haben Sie die Aspekte berücksichtigt, die Stockton meiner Meinung nach angesprochen hat, wo nur
die Vorteile des Versuchs werden angezeigt und er bittet darum, dass der weiche Unterleib
auch enthüllt werden? Ich fürchte, ich stimme ihm zu und - keine Beleidigung beabsichtigt -
dass Ihr Blog möglicherweise die gleichen Mängel aufweist.

Wenn nicht, dann stup' mich bitte an, gute Lektüre hat einen besonderen Platz
in meinem Leben :-)

Lucio.

@lootch Ich habe mein Bestes getan, um so viele Aspekte von "Versuchen" wie möglich (dh sowohl wenn es zutrifft als auch wenn nicht) in kurzer Codemenge zu zeigen und es so unvoreingenommen und realistisch wie möglich zu machen. Aber glaub mir natürlich nicht beim Wort :)

Dies war der am höchsten bewertete Kommentar zur zugehörigen Reddit-Diskussion :

Dies ist ein nützliches unvoreingenommenes hypothetisches Beispiel. Vielen Dank, dass Sie der Unterhaltung etwas Konstruktives hinzugefügt haben, das nicht nur "es scheiße" ist.

@lootch Ich habe mein Bestes getan, um so viele Aspekte von "Versuchen" wie möglich (dh sowohl wenn es zutrifft als auch wenn nicht) in kurzer Codemenge zu zeigen und es so unvoreingenommen und realistisch wie möglich zu machen. Aber glaub mir natürlich nicht beim Wort :)

Dies war der am höchsten bewertete Kommentar zur zugehörigen Reddit-Diskussion :

Dies ist ein nützliches unvoreingenommenes hypothetisches Beispiel. Vielen Dank, dass Sie der Unterhaltung etwas Konstruktives hinzugefügt haben, das nicht nur "es scheiße" ist.

Funktion mit Dateipfad als Argument? Dies allein wäre ein Grund, warum dieser Code meine Überprüfung nicht bestehen würde. Was ist, wenn einige Felder fehlen? Nachbestellt?

@sirkon Um nicht zu lang zu sein, ist das Beispiel natürlich vereinfacht. Die Änderungen, die erforderlich wären, um die von Ihnen angesprochenen Probleme zu beheben, wirken sich jedoch nicht auf die Fehlerbehandlungspraktiken aus, und das ist alles, was dort zählt.

Die Änderungen, die erforderlich wären, um die von Ihnen angesprochenen Probleme zu beheben, haben keinen Einfluss auf die Fehlerbehandlungspraktiken.

Weil du es gesagt hast?

  1. Beginnen Sie mit dem Titel Ihres Blogs: Es sollte "wie man nicht schreibt" heißen, denn, ich wiederhole, die Verwendung des Dateipfads als Parameter ist eine wirklich schlechte Praxis und, ehrlich gesagt, auch ein ganzer Code darunter.
  2. Ist dir das klar
    go resp := Respondent{ Name: name, Gender: try(parseField(s, &line, "gender")), OS: try(parseField(s, &line, "os")), Lang: try(parseField(s, &line, "lang")), }
    erzeugt schlechte Fehlermeldungen? Es sollte mindestens eine unerwartete Feldfehlermeldung und eine Fehlermeldung über fehlende Felder geben. Die Diagnose Ihres Skripts ist minderwertig.

PS Habe mir deine Repos angeschaut. Erkennen Sie, dass Go ein schlechtes Werkzeug für Ihre Aufgaben ist? Sie sollten verstehen, dass in der realen Anwendungspraxis von Go die ersten, die Protokolle sehen, Betriebsingenieure und keine Entwickler sind. Die richtige Fehlermeldung kann ihnen helfen, ein Problem selbst zu lösen.

@sirkon Komm schon, mach keine Flamewars.

Ist Ihnen klar, dass dies zu schlechten Fehlermeldungen führt?

Sie sind dem Modell vollkommen angemessen. Es wird erwartet, dass das Format alle Felder und deren Reihenfolge enthält. Die Fehlermeldung sagt es sehr deutlich.

Wenn Sie die Qualität des Codes bestreiten möchten, warum schreiben Sie ihn nicht nach Ihren Qualitätsstandards um? Wenn Sie dies tun, werde ich mein Bestes tun, um Ihren Code so umzuschreiben, dass er try .

PS Habe mir deine Repos angeschaut. Erkennen Sie, dass Go ein schlechtes Werkzeug für Ihre Aufgaben ist?

Schlagen Sie einen anderen für meine Aufgaben vor? Ich habe schon einige verwendet. Übrigens, das ist ziemlich off-topic.

@faiface

Schlagen Sie einen anderen für meine Aufgaben vor? Ich habe schon einige verwendet. Übrigens, das ist ziemlich off-topic.

Rost? C++?

@sirkon

Rost? C++?

Na, bitte. Ich habe beide benutzt, bevor ich mich mit Go niedergelassen habe. Ich habe nie zurückgeschaut.

@sirkon Einer der großen Fehler von try ist, dass es von der Fehlerdekoration abhält. Der Programmierer zeigte in diesem Fall mögliche Anwendungen von try , so dass natürlich nicht viel Fehlerdekoration stattfindet.

Außerdem ist es völlig unangebracht, Leute aufgrund der Projekte zu diskreditieren, an denen sie gearbeitet haben. Sie waren mit Ihren letzten paar Kommentaren ziemlich unhöflich, und ich möchte, dass Sie sich dessen zumindest bewusst sind.

@deanveloper Danke für den Kommentar!

Übrigens

Der Programmierer zeigte in diesem Fall mögliche Anwendungen von try, so dass natürlich nicht viel Fehlerdekoration stattfindet.

Falls Sie sich auf meinen Blog beziehen, gibt es tatsächlich eine Menge Fehlerdekoration, nur nicht genau so, wie @sirkon es tun würde. Hier sind einige Fehlermeldungen des Programms, das try :

parse respondnts.txt: open respondnts.txt: no such file or directory
parse respondents.txt: line 12: parse field gender: expected "gender:"
parse respondents.txt: line 9: expected empty line
parse respondents.txt: line 4: parse field lang: EOF

@faiface Mein Fehler, ich hätte genauer sein sollen. try rät von der Fehlerdekoration ab, wenn Sie mehrere Fehlermeldungen innerhalb derselben Funktion wünschen. Dies war mit check/handle Draft und mit den "Named Handler"-Gegenvorschlägen möglich. Es wäre in dem speziellen Fall, auf den hingewiesen wurde (in dem Sie beim Initialisieren einer Struktur try haben), sehr nützlich gewesen, um jede Nachricht Dekoration hinzufügen zu können, aber leider macht der Versuchsvorschlag dies etwas schwierig ohne eine eigene Funktion zu schreiben.

Check / Handle hätte in Ihrem speziellen Fall jedoch nicht so viel geholfen. Aber die vorgeschlagene Idee von catch und anderen Gegenvorschlägen zu try hätte die Fehler als zusätzliche Dekoration behandeln können.

@deanveloper Nun, meistens müssen Sie für alle Fehler innerhalb einer Funktion dieselbe Dekoration verwenden, da erwartet werden sollte, dass die Unterfunktionen ihren eigenen Kontext bereitstellen. Wenn Sie jedoch in einer einzigen Funktion Dinge anders dekorieren müssen, gibt es mit try immer noch eine einfache Lösung:

..., err := functionThatCanFail(...)
try(errors.Wrapf(err, ...))

Oder teilen Sie einfach die große Funktion in mehrere kleine auf.

@faiface in meinen Augen sollte man zu diesem Zeitpunkt einfach if err != nil , aber ich denke, es ist eine Frage der Präferenz.

Manchmal (wie im Fall der struct-initializing) ist es jedoch keine gute Idee, in mehrere Funktionen aufzuteilen. Ich werde aber wohl etwas pingelig.

Ich bin eigentlich nicht super gegen try , aber ich bin auch nicht gerade ein großer Unterstützer. Ich denke, dass es da draußen noch eine bessere Lösung gibt.

@deanveloper

Manchmal (wie im Fall der struct-initializing) ist es jedoch keine gute Idee, in mehrere Funktionen aufzuteilen.

Stimmt, aber es ist auch nicht erforderlich, sie anders zu dekorieren, da die gesamte erforderliche spezifische Dekoration von parseField .

Ich denke, dass es da draußen noch eine bessere Lösung gibt.

Das ist durchaus möglich! Wenn ich eine bessere Lösung sehe, lasse ich try in einer Minute fallen :)

meistens müssen Sie für alle Fehler innerhalb einer Funktion dieselbe Dekoration verwenden, da von den Unterfunktionen erwartet werden sollte, dass sie ihren eigenen Kontext bereitstellen

@faiface Ich stimme dieser Aussage vehement nicht zu. Jede Funktion ist eine Unterfunktion einer anderen in der Aufrufliste. Dies bedeutet, dass es die gleichen Verantwortlichkeiten im Fehlerbehandlungsfluss hat (d. h. Fehlerkontext für den oberen Bereich bereitstellen).

Stellen Sie sich eine Funktion vor, die zwei Datenblöcke an eine einzelne Datei anhängt. Wie würden Sie unterscheiden, welcher dieser Anhänge fehlgeschlagen ist, wenn Sie kaum eine Anweisung "in Datei schreiben konnte nicht schreiben" zurückgeben?

Wir sind alle faule Kreaturen. Auch ich würde es vorziehen, etwas ein für allemal zu tun, wenn ich könnte. Und ja, als ich mein Abenteuer mit Go begann, fand ich die Fehlerbehandlung etwas umständlich. Nach ein paar Jahren Übung drehte sich mein Blick jedoch um 180 Grad. Ich glaube, dass die derzeitige Fehlerbehandlung in Go eine verantwortungsvolle Programmierung und gutes Design fördert. IMHO wäre es ein großer Fehler, einen weiteren Mechanismus hinzuzufügen, der diesen Ansatz untergräbt.

@mklimuk Ein wichtiger Teil meines Kommentars ist "meistens". Das von Ihnen angegebene Beispiel wird wahrscheinlich am besten von if err != nil . Wie schon oft erwähnt, ist try nicht dafür ausgelegt, alle Situationen zu bewältigen, sondern nur die gängigsten.

Und Beweise zeigen, dass dies der Fall ist, da 70 % des gesamten Fehlerbehandlungscodes in der Standardbibliothek try out-of-the-box verwenden können und dasselbe gilt für 59 % des gesamten Fehlerbehandlungscodes in der wild.

@faiface Nun, die Tatsache, dass try die explizite Fehlerbehandlung ersetzen kann, bedeutet nicht, dass dies der Fall sein sollte. In meinem Fall ist das Zurückgeben eines Fehlers ohne Hinzufügen von Kontext nicht die "häufigste Situation". Es ist das Gegenteil :)

Leute, die diesen Thread positiv bewerten, sind nur besorgt, dass diese neue Aussage den ganzen Aufwand hinter dem ursprünglichen Go-Design (Einfachheit, Klarheit usw.) zunichte machen wird, um den Go-Code weniger ausführlich zu machen.

Sicher, aber ich hoffe, Sie verstehen, dass try nicht dazu dient, einen Fehler ohne Kontext zurückzugeben. Tatsächlich wird der häufigste Fall des Hinzufügens von Kontext (ein Kontext pro Funktion) durch try stark vereinfacht:

func doSomething() (err error) {
    defer fmt.HandleErrorf(&err, "doing something")

    x := try(oneThing())
    try(anotherThing(x))
    // ...
}

Es ist zu beachten, dass oneThing() und anotherThing() die meiste Zeit einen ausreichenden Kontext zurückgeben, so dass es völlig ausreichend ist, ihn in ein einfaches "doing something: ..." packen.

Als Randnotiz denke ich, dass wir einige Konventionen verwenden könnten, _wer_ die Dekoration macht. In der stdlib tun dies einige Funktionen, zB copy: x to y oder ähnliches, die Dekoration überlasse ich persönlich dem Aufrufer, da er die Argumente hat.

Wenn ich zum Beispiel ein Copy() hätte, würde ich etwas wie return errors.Wrap(err, "writing") und der Anrufer, der Copy() würde mit errors.Wrapf(err, "copying from %v to %v", src, dst) oder ähnlichem umbrechen.

Diese beiden lassen sich nicht so gut mischen und passen manchmal zu doppelten Strings. Ist es am besten, einfach zu sagen, dass der stdlib-Stil idiomatisch ist? Ich kann mich jedoch nicht erinnern, dass sich alle so verhalten haben. Ich denke, nur so wäre das Beispiel von

Ich persönlich überlasse die Dekoration dem Anrufer, da er die Argumente hat.

Jawohl. Betrachten Sie zum Beispiel eine Funktion, die den JSON-Body aus einer HTTP-Anforderung parst, die Header überprüft usw. Wenn es mit syntaktisch ungültigem JSON gefüttert wird, ist seine Verantwortung – wirklich alles, was es tun kann – den Fehler zurückzumelden. Der _Aufrufer_ weiß, welcher Teil der API versucht wurde, aufgerufen zu werden, und muss den Fehler entsprechend dekorieren, bevor er ihn entweder in der Kette weiterleitet oder einen HTTP-Fehler ausgibt.

Wenn Ihre Funktionen wirklich keinen Allzweckcode enthalten, der an mehreren Stellen verwendet werden könnte, verfügen sie nicht über die Informationen, die zum Dekorieren des Fehlers erforderlich sind. Im Gegensatz dazu, wenn sie den gesamten Kontext haben, sind es wahrscheinlich keine Funktionen, die als eigenständige Funktionen sinnvoll sind. Sie erstellen nur Funktionen, um den Code aufzuteilen und ihn besser organisiert aussehen zu lassen, als er tatsächlich ist.

@lpar Mind ein konkretes Beispiel dafür?

Ich habe schon ein konkretes Beispiel gegeben? Überlegen Sie, ob Ihre parseJSON-Funktion den Kontext tatsächlich kannte und in der Lage war, den API-Endpunkt und den Aktivitätsfluss zu dekorieren, für den sie den Körper parsen. Das würde bedeuten, dass es entweder spezifisch für diesen Endpunkt ist oder dass Sie die Informationen nur übergeben haben, damit Sie sie zum Umschließen von Fehlern verwenden können.

@lpar Okay, das ist ein weiteres Beispiel, in dem if err != nil weiterhin verwendet wird. Oder Sie teilen Ihre Logik in mehrere Funktionen auf.

Aber verstehen Sie, dass ein Beispiel, in dem try nicht angemessen ist, kein Argument gegen try . try soll nicht die gesamte Fehlerbehandlung ersetzen, sondern nur die häufigsten Fälle.

Screenshot 2019-07-07 at 6 30 42 PM

@abejide001 der try Vorschlag ist nicht das traditionelle "try/catch" aus vielen anderen Sprachen, es ist dem try! Makro in Rust viel ähnlicher. Gutes meme aber lol

Hoppla - in der falschen Ausgabe gepostet. Verschiebt nach https://github.com/golang/go/issues/32437#issuecomment -509024693.

Ich habe kürzlich einen Vorschlag Nr. 32968 gepostet, der auf meiner Ablehnung der gefährlichen Verschachtelungsfähigkeit des Makros try aufbaut. Obwohl ich hoffe, dass es keine ernsthaften Mängel enthält, bin ich als Autor nicht die richtige Person, um welche zu sehen. Daher möchte ich mein _nicht versuchen_ Lager (Sie :) bitten, es zu sehen, zu bewerten und zu kommentieren.


Auszug:

  • _Das check Makro ist kein Einzeiler: Es hilft am meisten, wenn sich viele wiederholen
    Prüfungen, die denselben Ausdruck verwenden, sollten in unmittelbarer Nähe durchgeführt werden._
  • _Seine implizite Version wird bereits auf dem Spielplatz

Konstruktionsbeschränkungen (erfüllt)

Es ist ein eingebautes Programm, es verschachtelt sich nicht in einer einzelnen Zeile, es erlaubt weit mehr Flüsse als try und hat keine Erwartungen an die Form eines darin enthaltenen Codes. Es ermutigt nicht zur nackten Rückkehr.

Anwendungsbeispiel

// built-in 'check' macro signature: 
func check(Condition bool) {}

check(err != nil) // explicit catch: label.
{
    ucred, err := getUserCredentials(user)
    remote, err := connectToApi(remoteUri)
    err, session, usertoken := remote.Auth(user, ucred)
    udata, err := session.getCalendar(usertoken)

  catch:               // sad path
    ucred.Clear()      // cleanup passwords
    remote.Close()     // do not leak sockets
    return nil, 0, err // dress before leaving
}
// happy path

// implicit catch: label is above last statement
check(x < 4) 
  {
    x, y = transformA(x, z)
    y, z = transformB(x, y)
    x, y = transformC(y, z)
    break // if x was < 4 after any of above
  }

Hoffe das hilft, viel Spaß!

Aber verstehen Sie, dass ein Beispiel, in dem try nicht angemessen ist, kein Argument gegen try . try soll nicht die gesamte Fehlerbehandlung ersetzen, sondern nur die häufigsten Fälle.

Und gemäß den Statistiken, die ich zuvor gepostet habe , sind dies nicht die häufigsten Fälle in meinem Code. Die häufigsten Fälle in meinem Code sind Fehler, die vor der Rückgabe umschlossen werden. try wäre also bestenfalls für einen einstelligen Prozentsatz meiner Fehlerrückgaben geeignet (*), weshalb ich denke, dass wir etwas Besseres brauchen.

(*) Und tatsächlich neige ich zu der Annahme, dass die nackten err Rückgabeinstanzen wahrscheinlich Fehler sind, die behoben werden sollten.

Stimme voll und ganz zu, lass "if err != nil" in Ruhe.

@abejide001 der try Vorschlag ist nicht das traditionelle "try/catch" aus vielen anderen Sprachen, es ist dem try! Makro in Rust viel ähnlicher. Gutes meme aber lol

Dies allein ist mir ein Anliegen, Go ist für Neulinge bereits eine seltsame Sprache, und jetzt müssen wir erklären, warum try eine maßgeschneiderte Logik hat. FWIW, ich glaube nicht, dass die Aussage "Rust hat es getan" ein guter Grund ist, etwas in eine Sprache aufzunehmen - es ist einfach nicht bekannt.

@da ich das nicht gesagt habe, um die Funktion zu rechtfertigen, habe ich es nur gesagt, um zu verdeutlichen, was die Funktion try Vorschlag.

Nicht, dass dies einen großen Unterschied macht, aber es ist auch ein Feature in Swift, allerdings mit einem Schlüsselwort anstelle eines Makros.

Es scheint, dass es einige Verwirrung darüber gibt, was genau mit dem Versuch erreicht werden soll. IMHO, das Problem besteht nicht darin, mehrere if-Blöcke zu schreiben, die auf Fehler prüfen. Sie schreiben diese einmal und Sie sind fertig. Das Problem besteht darin, Code zu lesen, der mehrere dieser Blöcke enthält. Wir lesen viel mehr als schreiben. Und diese Blöcke verschleiern den eigentlichen Code, weil sie sich mit ihm verflechten. Schlimmer noch, oft sind sie fast genau gleich, mit nur einem geringfügigen String-Unterschied irgendwo innerhalb dieses if-Blocks.

Ich persönlich habe den alten Scheck-Handle-Entwurf bevorzugt, aber dieser trennt zumindest Fehler und Geschäftspfade gut. Und wir könnten endlich in der Lage sein, einen einzigen Funktionsbereichskontext zu haben, im Gegensatz zu jedem Aufruf, der derzeit eine gute Chance hat, dasselbe wie den übergeordneten Fehler zu wiederholen.

@icholy schrieb:

Es gab überwältigendes Feedback der Community, das eine optimierte Fehlerbehandlung forderte (aus der jährlichen Umfrage). Das Go-Team befasst sich nun mit diesem Problem.

Ich habe gerade die Umfrage hier nachgeschlagen: https://blog.golang.org/survey2018-results

Offenbar lautete die Frage: "Was ist die größte Herausforderung, vor der Sie heute persönlich mit Go stehen?" mit möglicher Antwort "Fehlerbehandlung".

Ich frage mich ernsthaft, wie aus dieser Frage + Antwort abgeleitet wurde, dass eine kürzere Syntax erforderlich war. Ich hätte vielleicht auch auf 'Fehlerbehandlung' geantwortet, aber auf keinen Fall hätte ich eine andere Syntax sehen wollen. Hätte ich diese Option in der Umfrage angekreuzt, hätte ich mir überlegt, Fehler besser einzuschließen, sie mit Stack-Traces zu versehen usw.

Mein Vorschlag wäre, von allen Vorschlägen zur Fehlerbehandlung zurückzutreten (effektiv, was @miekg vorgeschlagen hat ). Und erst einmal feststellen, was die Community eigentlich will, dokumentiert das. Dann finden Sie heraus, warum sie das wollen. Und fangen Sie erst danach an, nach Wegen zu suchen, dies zu erreichen.

Ich bin gerade den try-Vorschlag durchgegangen, aber es sei denn, ich fehle etwas, es versäumt es zu sagen, _warum_ es vorgeschlagen wird, außer "die Boilerplate if-Anweisungen zu eliminieren [...}". Es wird jedoch nicht erwähnt, warum die Eliminierung dieser Boilerplate-wenn-Anweisungen erforderlich ist.

Dem oben genannten stimme ich auf jeden Fall zu. Sehen wir uns an, ob die neuen Fehlerwertänderungen dazu beitragen, die Beschwerden bei der Fehlerbehandlung zu unterstützen, die Leute mit Go haben. Dann können wir sehen, ob eine kürzere Syntax erforderlich ist.

Die Leute hier argumentieren gegen try weil sie der Meinung sind, dass alle zurückgegebenen Fehler mit Anmerkungen versehen werden sollten. Die Realität ist, dass im aktuellen Codekorpus (einschließlich der Standardbibliothek) ein hoher Prozentsatz der Fehlerprüfungen ~nackte~ nicht kommentierte Fehlerrückgaben haben und von try profitieren würden. Ihre Überzeugung, wie Code sein SOLLTE , hat nichts damit zu tun, wie Code IST . Erspar mir dein Dogma.

@icholy Das Ignorieren von Fehlern zeigt an, dass der

Mein Vorschlag wäre, von allen Vorschlägen zur Fehlerbehandlung zurückzutreten (effektiv, was @miekg vorgeschlagen hat ). Und erst einmal feststellen, was die Community eigentlich will, dokumentiert das. Dann finden Sie heraus, warum sie das wollen. Und fangen Sie erst danach an, nach Wegen zu suchen, dies zu erreichen.

Dem stimme ich ausdrücklich zu. Ich sehe eine Menge grundlegender Meinungsverschiedenheiten darüber, welche Funktionalität eine Verbesserung der Go-Fehlerbehandlung überhaupt unterstützen sollte. Jeder andere Teil der Funktionalität, den die Leute erwähnen, löst Bikeshedding aufgrund seiner Benennung und Syntax aus, so dass die Diskussion nirgendwo hinführt.

Ich würde gerne genauer wissen, was die breitere Go-Community von jedem vorgeschlagenen neuen Fehlerbehandlungsfeature erwartet.

Ich habe eine Umfrage zusammengestellt, die eine Reihe verschiedener Funktionen auflistet, Teile von Fehlerbehandlungsfunktionen, die ich von Leuten vorgeschlagen habe. Ich habe jede vorgeschlagene Benennung oder Syntax sorgfältig _weggelassen_ und natürlich versucht, die Umfrage neutral zu gestalten, anstatt meine eigene Meinung zu bevorzugen.

Wenn Leute mitmachen möchten, hier der Link, gekürzt zum Teilen:

https://forms.gle/gaCBgxKRE4RMCz7c7

Jeder Teilnehmer sollte die zusammenfassenden Ergebnisse sehen können. Wenn wir dann vielleicht eine bessere Vorstellung davon haben, was die Leute tatsächlich wollen, können wir eine intelligente Diskussion darüber führen, ob der Versuchsvorschlag diese Dinge bietet. (Und dann vielleicht sogar mit der Diskussion über die Syntax fortfahren.)

@lane-c-wagner versuchen Sie zu sagen, dass die Rückgabe eines nicht kommentierten Fehlers dasselbe ist, als würde sie überhaupt nicht zurückgegeben? edit: vorherigen Kommentar korrigiert

@icholy Ah, das habe ich falsch verstanden. Als Sie "nackt" sagten, dachte ich, Sie meinen, "_" ignorierte Fehler.

Dieser Vorschlag argumentiert, dass keine Handlung eine gültige Handlung sein sollte. Diese Änderung betrifft alle Benutzer der Sprache, da sie den Code lesen. Daher muss bei einer Umfrage, die die größte Hürde identifiziert, die Community immer noch gefragt werden, ob es sich lohnt, diese Hürde zu nehmen. Dieser Vorschlag ist die engste Bewertung einer solchen Frage.

Bitte hör auf zu sagen "dass es jedem freisteht zu ignorieren" try . Wir lesen Code, der von anderen geschrieben wurde.

@tv42 Ich weiß nicht, ob Sie mich hier ansprechen, aber das habe ich auch gesagt, und Sie haben Recht . Schuldig im Sinne der Anklage. Ich werde versuchen, mit solchen Verallgemeinerungen vorsichtiger zu sein. Vielen Dank.

@griesemer deine Umfrage hat stark gefehlt. Ich habe für die Fehlerbehandlung gestimmt, aber das Problem, das ich meinte, war die vollständige Typsicherheit, nicht die Ausführlichkeit. Machen Sie besser einen weiteren nur über Fehler.

Und ich will immer noch Summentypen.

Dies ist ein Vorschlag über die Art und Weise, wie gofmt derzeit formatiert, wenn err != nil

(Dies ist keine Meinung zum try()-Vorschlag.)

Wenn eine if-Anweisung einen einzeiligen Nicht-Null-Fehlerwert zurückgibt, wie zum Beispiel:

err := myFunc()
if err != nil {
    return err
}

gofmt könnte seine eigene if-Anweisungsregel lockern und in einer Zeile wie folgt formatieren:

err := myFunc()
if err != nil { return err }

Aus drei Zeilen Fehlerbehandlungscode wird nur eine Zeile. Weniger Unordnung. Programmablauf leichter zu verfolgen.

Es muss ein gewisses Urteilsvermögen darüber gegeben werden, wo die Grenze (Wortspiel anerkannt) damit gezogen werden soll
Änderung der gofmt-Regel. Es kann einige Dekorationen enthalten, wie zum Beispiel:

err := myFunc()
if err != nil { return fmt.Errorf("myFunc() blew up! %v", err }

Aber die aufwändige mehrzeilige Fehlerbehandlung soll so bleiben, wie sie ist: mehrzeilig und klar und eindeutig.

Der _try_-Vorschlag wurde zurückgezogen: https://github.com/golang/go/issues/32437#issuecomment -512035919

Generika jemand?

Dies ist ein Vorschlag über die Art und Weise, wie gofmt derzeit formatiert, wenn err != nil

Das habe ich versucht, imho ist der Code so noch unleserlicher als bei mehrzeiliger Formatierung. versuchen ist viel besser als diese Lösung.

IMO ist das Problem hier eher nicht, wie die Fehlerbehandlung durchgeführt wird, sondern ob sie ignoriert wird . Wäre es nicht möglich, die Syntax von if err != nil unverändert zu belassen, aber die Unkenntnis der Rückgabewerte von Error einzuschränken? Machen Sie es zum Beispiel zu einer Compiler-Warnung / einem Compiler-Fehler mit der Option "Deseverity" für den Legacy-Code.

IMO ist das Problem hier eher nicht, wie die Fehlerbehandlung durchgeführt wird, sondern ob sie ignoriert wird . Wäre es nicht möglich, die Syntax von if err != nil unverändert zu belassen, aber die Unkenntnis der Rückgabewerte von Error einzuschränken? Machen Sie es zum Beispiel zu einer Compiler-Warnung / einem Compiler-Fehler mit der Option "Deseverity" für den Legacy-Code.

Viele Leute wollen einen Linter, der ignorierte Fehler anzeigt.

Ich würde es vorziehen, dies zu einem harten Fehler zu machen, aber wenn man sich die Tonnen von bereits geschriebenem Erbe ansieht, ist Linter auch fair.

Ich finde https://github.com/kisielk/errcheck wertvoll, um mir über unbehandelte Fehler zu informieren @plyhun @sorenvonsarvort

Wie aus der Diskussion zu Nr. 32437 hervorgeht, wurde dieser Vorschlag vorerst angenommen. Schließen. Tritt das Problem erneut auf, kann ein neuer Vorschlag eröffnet werden.

Ich fange an zu denken, dass einer der Gründe dafür, dass viele der Vorschläge nicht richtig passen, darin besteht, dass sie tatsächlich versuchen, zwei verschiedene Probleme gleichzeitig anzugehen. Einerseits stimmt es, dass err != nil Blöcke nach fast jedem Funktionsaufruf den Codefluss auf seltsame Weise unterbrechen können, obwohl es sicherlich seine Vorteile hat, aber ich denke, das ist nur die Hälfte der Problem. Das andere Problem ist, dass die Handhabung mehrerer Rücksendungen, unabhängig davon, ob Fehler aufgetreten sind oder nicht, ziemlich umständlich sein kann.

Mehrere Rückgabefunktionen fühlen sich sehr, sehr anders an als einzelne Rückgabefunktionen, trotz des scheinbar kleinen Unterschieds zwischen den beiden. Es ist so, als ob es zusätzliche Einschränkungen beim Aufrufen von Funktionen gäbe, die mehr als ein Argument benötigen. Es fühlt sich manchmal sehr seltsam an, damit umzugehen. Wenn Sie eine Funktion mit mehreren Rückgabewerten aufrufen, müssen Sie dies fast immer in einer eigenen Zeile tun, und sie ist in Kombination mit := oft die Hauptursache für die verschiedenen Variablen-Shadowing-Probleme, die an anderer Stelle diskutiert wurden . Sie können keine Methodenaufrufe an sie verketten, Sie können sie nicht direkt einem Strukturfeld und einer neuen Variablen in derselben Zeile zuweisen und so weiter.

Ich weiß nicht. Vielleicht liegt es nur an mir. Aber ich benutze Go seit fast 10 Jahren und das Aufrufen von Funktionen mit mehreren Rückgaben fühlt sich für mich manchmal immer noch irgendwie unangenehm an.

Dankeschön!

Es gibt tatsächlich ein Problem mit if err != nil , der Umfang von err kann länger leben, als er sollte. Wenn Sie if einreihen, wird das Problem gelöst, aber nicht alle Fälle können eingefügt werden.

if err := foo(); err != nil {
if _, err := bar(); err != nil {



md5-6a135eb952fe7b24b3389cb16d3244a1



a, err := bar()
if err != nil {



md5-d52f811d3e31bb368bd8045cfb2e93b4



var err error
baz.A, err = bar()
if err != nil {

Die Variable err sollte nicht im Funktionsumfang vorhanden sein, nachdem der Block if err != nil {} abgeschlossen ist. Hier ist mein Vorschlag, der auf dem Vorschlag von try() aufbaut, um das Problem zu beheben https://github.com/golang/go/issues/33161. Ich würde mich über ein konstruktives Feedback freuen.

Die err-Variable sollte nicht im Funktionsumfang vorhanden sein, nachdem der if err != nil {}-Block abgeschlossen ist.

warum "sollte" es nicht existieren, nachdem der if-Block abgeschlossen ist? Der Compiler kann dafür optimieren (wenn er dies für notwendig hält), und es gibt keine mentale Belastung, wenn der err := stmt()\nif err != nil {}-Block abgeschlossen wird, da diese fast immer zusammenpassen.

Ich habe mir Ihren Vorschlag noch nicht eingehend angesehen (obwohl ich ein großes Lob dafür gebührt, dass Sie sich die Mühe gemacht haben, einen zu schreiben!). Wie ich jedoch auch in meinem obigen Kommentar dargelegt habe, denke ich, dass mehr Forschung zu allen wahrgenommenen Problemen erforderlich ist, bevor wir uns mit Vorschlägen zu ihrer Lösung befassen.

@Freeaqingme- Fehler sollten nicht mehr vorhanden sein, nachdem der if err != nil Block abgeschlossen ist, hauptsächlich weil wir uns bereits so verhalten, als ob dies nicht der Fall wäre.

Im CopyFile-Beispiel gibt es r, err := os.Open(src) gefolgt von w, err := os.Create(dst) . Der zweite err beschattet den ersten. Das Shadowing von Variablen ist normalerweise verpönt.

Es gibt auch andere Kuriositäten. Wenn ich err := foo() und später etwas wie bar.V, err = baz() , wenn der Code umgestaltet wird und ich foo() nicht mehr benötige, müsste ich var err error vor dem baz hinzufügen

Technisch in

    r, err := os.Open(src)
    if err != nil {
        return ...
    }
    w, err := os.Create(dst)

die zweite Instanz von err überschattet nicht die erste Instanz. Sie sind tatsächlich dieselbe Variable. Siehe die Diskussion zum Umdeklarieren von Variablen unter https://golang.org/ref/spec#Short_variable_declarations.

func doSomeThing() {
r, err := os.Open(Dateiname)
panic(fmt.Errorf(err, "Datei konnte nicht geöffnet werden: %s", Dateiname)) //
Hier herrscht Panik.

}

Am Do, 10.10.2019 um 11:24 Uhr schrieb clearcode [email protected] :

Ich denke, wir können eine Build-In-Funktion hinzufügen:

behaupten()

Beispiel:

func doSomeThing() Fehler {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

bzw,err := http.Get(someURL)
assert(err, "Anfrage fehlgeschlagen")

}

und eine andere Funktion, die keinen Fehler zurückgibt:

func doSomeThing() {
r, err := os.Open(Dateiname)
assert(err, "Datei konnte nicht geöffnet werden: %s", Dateiname) // Hier herrscht Panik.

}

also assert(error, args ...interface{}) ist besser als: if err != nil ; {
Rückgabefehler }


Sie erhalten dies, weil Sie einen Kommentar abgegeben haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/golang/go/issues/32825?email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJmentcom
oder abmelden
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

Die Erkenntnis ist, dass ich den tatsächlichen Fehler sehen möchte, der im Strom zurückgegeben wird
Funktion in der aktuellen Zeile.

Am Freitag, 11. Oktober 2019 um 9:55 Uhr schrieb Aaaa Einai [email protected] :

func doSomeThing() {
r, err := os.Open(Dateiname)
panic(fmt.Errorf(err, "Datei konnte nicht geöffnet werden: %s", Dateiname)) // Hier herrscht Panik.

}

Am Do, 10.10.2019 um 11:24 Uhr clearcode [email protected]
schrieb:

Ich denke, wir können eine Build-In-Funktion hinzufügen:

behaupten()

Beispiel:

func doSomeThing() Fehler {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

bzw,err := http.Get(someURL)
assert(err, "Anfrage fehlgeschlagen")

}

und eine andere Funktion, die keinen Fehler zurückgibt:

func doSomeThing() {
r, err := os.Open(Dateiname)
assert(err, "Datei konnte nicht geöffnet werden: %s", Dateiname) // Hier herrscht Panik.

}

also assert(error, args ...interface{}) ist besser als: if err != nil ; {
Rückgabefehler }


Sie erhalten dies, weil Sie einen Kommentar abgegeben haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/golang/go/issues/32825?email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJmentcom
oder abmelden
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

Ehrlich gesagt möchte ich keine implizite Rückgabe, die try liefert. Wenn wir Generika hätten, würde ich eine Lösung bevorzugen, die stattdessen monadisches Verhalten verwendet.

type Result<T> interface {
  Expect(err error) T
  OrElse(defaultValue T) T
}

func From<T>(value T, err error) Result<T> { ... }

Für mich ist dies viel sauberer als das derzeit vorgeschlagene integrierte, obwohl weitere Änderungen an den oben genannten erforderlich wären, da Sie eine Vielzahl von Methoden haben, die (Wert, Fehler) und Ergebnis zurückgeben

Es ist so ähnlich wie Rust's Ok und Err.
Ich denke, if err != nil {} vielleicht ein bisschen besser.

@Yanwenjiepy das ist beabsichtigt, ich bin ein großer Fan von Rusts Result Typ.

Ich lerne Go in weniger als 10 Minuten. Das allererste, was mir in dem Code auffiel, den ich mir ansah, war, dass diese Kopie immer und immer wieder eingefügt wurde:

someValue, err := someFunction();
if err != nil {
  panic(err)
}

Ich bin natürlich kein Experte, aber es könnte von Wert sein, dass ich nur meinen ersten Blick gebraucht habe, um in diesem Thread zu landen.

Das liegt daran, dass Sie sich Code-Snippets zum Lernen ansehen. Echter Code muss mit Fehlern umgehen, nicht nur mit Panik und Abstürzen.

Stimmt, aber Fehler können (und sollten oft) gruppiert werden. Aus diesem Grund gibt es Try/Catch-Blöcke in anderen Sprachen. Folgendes würde zum Beispiel für mich viel weniger nach Dinosauriern riechen:

try {
  foo, throw err := someFunction();
  bar, throw err := foo.get();
  baz, throw err := bar.make();
  qux, throw err := baz.transform();
} catch(err) {
  // "Unable to foo bar baz qux."
  tryHarder();
}

Wieder einmal totaler Laie. Aber Code besteht nur aus Symbolen, und wenn sie sich genug wiederholen, können Sie auch dafür ein Symbol erstellen. Dies scheint ein sich sehr häufig wiederholendes Symbol zu sein.

Vielleicht möchten Sie einen Blick auf den Beitrag Errors Are Values ​​von Rob Pike werfen, um zu sehen, wie Sie einen Helfer verwenden können, um Fehler zusammenzuführen und sie alle gleichzeitig zu behandeln. In der Praxis wird das Abfangen aller Ausnahmen mit einer einzigen Klausel in den meisten Sprachen, die sie haben, als schlechter Stil angesehen, da Sie am Ende Informationen darüber verbergen, was tatsächlich passiert ist. (Und wenn Sie das Beispiel erweitern, um die einzelnen abgefangenen Ausnahmen herauszulösen und diese Informationen nicht wegzuwerfen, endet der Code so lange wie das Go-Äquivalent.)

Danke für den Link. Das errWriter ist eine völlig passable Lösung.

Stimmt, aber Fehler können (und sollten oft) gruppiert werden. Aus diesem Grund gibt es Try/Catch-Blöcke in anderen Sprachen. Folgendes würde zum Beispiel für mich viel weniger nach Dinosauriern riechen:

try {
  foo, throw err := someFunction();
  bar, throw err := foo.get();
  baz, throw err := bar.make();
  qux, throw err := baz.transform();
} catch(err) {
  // "Unable to foo bar baz qux."
  tryHarder();
}

Wieder einmal totaler Laie. Aber Code besteht nur aus Symbolen, und wenn sie sich genug wiederholen, können Sie auch dafür ein Symbol erstellen. Dies scheint ein sich sehr häufig wiederholendes Symbol zu sein.

Nehmen wir an, jede Funktion gibt einen überlappenden Fehlertyp zurück und Sie müssen alle Funktionsergebnisse elegant behandeln. Wie schreibt man tryHarder()?

try {
  foo, throw err := someFunction();  // err could be TypeA and TypeB
  bar, throw err := foo.get();       // err could be TypeB and TypeC
  baz, throw err := bar.make();      // err could be TypeA and TypeC
  qux, throw err := baz.transform(); // err could be TypeB and TypeD
} catch(err) {
  tryHarder(); // tell me how to handle each error?
}

Es dauert nur 1 Minute, um den folgenden Code zu verstehen:

foo, err := someFunction();  // err could be TypeA and TypeB
if err != nil {
 // handle err
}

bar, err := foo.get();       // err could be TypeB and TypeC
if err != nil {
  // handle err
}

baz, err := bar.make();      // err could be TypeA and TypeC
if err != nil {
  // handle err
}

qux, err := baz.transform(); // err could be TypeB and TypeD
if err != nil {
  // handle err
}

Nehmen wir an, jede Funktion gibt einen überlappenden Fehlertyp zurück und Sie müssen alle Funktionsergebnisse ordnungsgemäß behandeln

In diesem Beispiel liegst du völlig richtig.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen