2016-04-27 18 views
11

In Rust glaube ich, dass die idiomatische Methode, mit behebbaren Fehlern umzugehen, Ergebnis ist. Zum Beispiel ist diese Funktion eindeutig idiomatische:Was ist der idiomatische Weg, um einen Fehler von einer Funktion ohne Ergebnis zurückzugeben, wenn erfolgreich?

fn do_work() -> Result<u64, WorkError> {...} 

Natürlich gibt es auch Funktionen, die einen einzelnen, klar, Fehlerzustand haben, und daher die Option Typ stattdessen verwenden. Ein idiomatisches Beispiel wäre dies:

fn do_work() -> Option<u64> 

Das alles ist ohne weiteres in der Dokumentation behandelt. Ich bin jedoch verwirrt über den Fall, in dem eine Funktion fehlschlagen kann, hat aber keinen sinnvollen Wert, wenn sie erfolgreich ist. Vergleichen Sie die folgenden zwei Funktionen:

fn do_work() -> Option<WorkError> 
// vs 
fn do_work() -> Result<(), WorkError> 

Ich bin nur nicht sicher, welche eine von diesen mehr idiomatische ist oder häufiger in der realen Welt Rust-Code verwendet. Meine Anlaufstelle für Fragen wie diese ist das Rust-Buch, aber ich glaube nicht, dass dies in der Rubrik "Error Handling" angesprochen wird. Ich habe auch mit keiner anderen Rust-Dokumentation viel Glück gehabt.

Natürlich scheint das ziemlich subjektiv, aber ich suche nach autoritativen Quellen, die entweder angeben, welche Form idiomatisch ist, oder warum eine Form der anderen überlegen (oder schlechter) ist. (Ich bin auch neugierig, wie die Konvention im Vergleich zu anderen Sprachen, die "Fehler als Werte", wie Go und Haskell verwenden.)

+2

Ich bin der 'Ergebnis <(), Error>' Seite der Dinge .. Ich alias diese auch als meine eigenen Typen. Ich würde gerne hören, was andere sagen. Ich mache das aber, weil das 'try!' Makro immer noch sehr gut damit spielt. –

Antwort

17

Verwenden Sie fn do_work() -> Result<(), WorkError>.

Result<(), WorkError> bedeutet, dass Sie möchten, dass die Arbeit ausgeführt wird, aber es kann fehlschlagen.

Option<WorkError> bedeutet, dass Sie einen Fehler erhalten möchten, aber möglicherweise nicht vorhanden ist.

Sie wollen wahrscheinlich die Arbeit erledigt werden, aber nicht einen Fehler zu bekommen, wenn Sie do_work() schreiben, also Result<(), WorkError> ist die bessere Wahl.

Ich würde erwarten, Option<WorkError> nur in Fällen wie fn get_last_work_error() -> Option<WorkError> verwendet werden.

+0

Ich glaube nicht, dass ich anderer Meinung bin, ich denke, Ergebnis <(), WorkError> ist etwas schöner. Ich frage mich jedoch, ob es "offizielle" Quellen gibt, die Ihre Antwort in irgendeiner Weise unterstützen. – Others

+0

@Others Da 'do_work' selbst ein konzeptionelles Beispiel ist, erwarte ich nicht, dass es eine offizielle Konvention dafür geben wird. Aber Sie können ein Gefühl für das Idiom in der Standardbibliothek bekommen, zum Beispiel: https://doc.rust-lang.org/nightly/std/io/trait.Read.html#method.read_exact – WiSaGaN

+4

@Other die gesamte Standard-Bibliothek verwendet 'Ergebnis <(), _>' pervasiv, wenn eine Operation, die fehlschlagen kann, nichts Nützliches zurückgibt (zB viele Funktionen von 'std :: io' und' std :: fs'). Es ist die richtige Wahl. – huon

4

Rust ist "ziemlich stark typisiert" (und bitte, rufen Sie mich nicht an, wie ich messe, wie stark eine Sprache typisiert ist ...). Ich meine das in dem Sinne, dass Rust Ihnen im Allgemeinen die Werkzeuge gibt, damit Typen für Sie "sprechen" und Ihren Code dokumentieren. Daher ist es idiomatisch, diese Funktion zu verwenden, um lesbaren Code zu schreiben.

Mit anderen Worten, die Frage, die Sie stellen, sollte mehr sein "welcher Typ repräsentiert am besten, was die Funktion für jeden tut, der seine Unterschrift liest?"

Für Result<(), Workerror> Sie gerade sehen from the docs

Ergebnis ein Typ ist, der entweder Erfolg (Ok) oder Fehler (Err) Also, spezialisiert für Ihren Fall stellt

, bedeutet dies, Ihre Funktion gibt nichts zurück, wenn es erfolgreich ist (dargestellt durch Ok<()>) oder WorkError, wenn ein Fehler vorliegt (Err<WorkError>). Dies ist eine sehr direkte Darstellung der Art und Weise, wie Sie die Funktion in Ihrer Frage beschrieben haben.

Vergleichen Sie dies mit Option<WorkError> oder Option<()>

Typ Option einen optionalen Wert darstellt: Jede Option ist entweder etwas aus und enthält einen Wert oder ohne, und nicht

In Ihrem Fall Option<WorkError> wäre sagen dem Leser "diese Funktion sollte eine WorkError zurückgeben, aber es kann nichts zurückgeben". Sie können dokumentieren, dass der "Nichts zurückgeben" -Fall bedeutet, dass die Funktion tatsächlich erfolgreich war, aber das ist bei Typen nicht sehr offensichtlich.

Option<()> sagt „diese Funktion nichts zurückgeben kann oder keine sinnvolle Rückkehr“, die eine vernünftige Sache sein kann, zu sagen, wenn WorkError keine andere Informationen enthält (wie ein Fehlertyp oder eine Fehlermeldung) und es ist praktisch nur eine Möglichkeit, sagen Sie "ein Fehler ist aufgetreten". In diesem Fall enthält eine einfache bool die gleiche Information ... Ansonsten können Sie mit der Result weitere Informationen zu dem Fehler zurückgeben.