web-dev-qa-db-fra.com

Quelle est la manière idiomatique de renvoyer une erreur d'une fonction sans résultat en cas de succès?

Dans Rust, je crois que la façon idiomatique de traiter les erreurs récupérables consiste à utiliser Result. Par exemple, cette fonction est clairement idiomatique:

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

Bien sûr, il existe également des fonctions qui ont un état d'échec unique et évident et qui utilisent donc le type Option à la place. Un exemple idiomatique serait le suivant:

fn do_work() -> Option<u64>

Tout cela est directement abordé dans la documentation. Cependant, je suis confus au sujet du cas où une fonction peut échouer, mais n'a aucune valeur significative en cas de succès. Comparez les deux fonctions suivantes:

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

Je ne suis tout simplement pas sûr de savoir lequel est le plus idiomatique ou le plus utilisé dans le monde réel Rust code. Ma ressource de référence pour des questions comme celle-ci est le Rust book, mais je ne pense pas que cela soit traité dans sa section " Error Handling ". Je n'ai pas eu beaucoup de chance avec d'autres Rust = documentation non plus.

Bien sûr, cela semble assez subjectif, mais je recherche des sources faisant autorité qui indiquent soit quelle forme est idiomatique, soit pourquoi une forme est supérieure (ou inférieure) à l'autre. (Je suis également curieux de voir comment la convention se compare à d'autres langages qui utilisent fortement les "erreurs comme valeurs", comme Go et Haskell.)

31
Others

Utilisez fn do_work() -> Result<(), WorkError>.

Result<(), WorkError> signifie que vous voulez que le travail soit fait, mais il peut échouer.

Option<WorkError> Signifie que vous souhaitez obtenir une erreur, mais elle peut être absente.

Vous voulez probablement que le travail soit fait mais pas pour obtenir une erreur lorsque vous écrivez do_work(), donc Result<(), WorkError> est le meilleur choix.

Je m'attendrais à ce que Option<WorkError> Ne soit utilisé que dans des cas comme fn get_last_work_error() -> Option<WorkError>.

26
WiSaGaN

La rouille est "assez fortement typée" (et s'il vous plaît, ne m'appelez pas sur la façon dont je mesure à quel point une langue est typée ...). Je veux dire cela dans le sens où Rust vous donne généralement les outils pour laisser les types "parler" pour vous et documenter votre code, il est donc idiomatique d'utiliser cette fonctionnalité pour écrire du code lisible.

En d'autres termes, la question que vous posez devrait être plus "quel type représente le mieux ce que la fonction fait à quiconque lit sa signature?"

Pour Result<(), Workerror> vous pouvez voir directement à partir des documents

Le résultat est un type qui représente le succès (Ok) ou l'échec (Err)

Donc, spécialisé pour votre cas, cela signifie que votre fonction ne retourne rien si elle réussit (représentée par Ok<()>) ou WorkError s'il y a une erreur (Err<WorkError>). Il s'agit d'une représentation très directe dans le code de la façon dont vous avez décrit la fonction dans votre question.

Comparez cela à Option<WorkError> Ou Option<()>

Type Option représente une valeur facultative: chaque option est soit Some et contient une valeur, soit None, et ne contient pas

Dans votre cas, Option<WorkError> Dirait au lecteur "cette fonction doit renvoyer un WorkError mais elle peut ne rien retourner". Vous pouvez documenter que le cas "ne rien renvoyer" signifie que la fonction a réussi, mais ce n'est pas très évident à partir des types seuls.

Option<()> dit "cette fonction ne peut rien retourner ou n'a pas de retour significatif", ce qui peut être une chose raisonnable à dire si WorkError ne contient aucune autre information (comme un type d'erreur ou un message d'erreur) et ce n'est pratiquement qu'une façon de dire "une erreur s'est produite". Dans ce cas, un simple bool contient les mêmes informations ... Sinon, le Result vous permet de renvoyer plus d'informations associées à l'erreur.

6
Paolo Falabella