From e8be56fade27e3ebd77bd3f04c8a3e6c9197b1ef Mon Sep 17 00:00:00 2001 From: Augustin ROUSSET-ROUVIERE Date: Sun, 25 Aug 2024 17:57:19 +0200 Subject: [PATCH] Improving error handling's best practices `+` Test to comply with RFC1236 `+` Error API recommendation `+` French and english version --- src/en/04_language.md | 46 ++++++++++++++++++++++++++++++++++++------- src/fr/04_language.md | 46 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/src/en/04_language.md b/src/en/04_language.md index 8491118..a28d2d1 100644 --- a/src/en/04_language.md +++ b/src/en/04_language.md @@ -112,25 +112,57 @@ else { println!("{}", res); } > specialized functions `overflowing_`, `wrapping_`, or the > `Wrapping` type must be used. - - ## Error handling - - The `Result` type is the preferred way of handling functions that can fail. A `Result` object must be tested, and never ignored. +> ### Recommendation {{#check LANG-ERRDO | Use the `?` operator and do not use the `try!` macro}} +> +> The `?` operator should be used to improve readability of code. +> The `try!` macro should not be used. + +### Custom Error type implementation + > ### Recommendation {{#check LANG-ERRWRAP | Implement custom `Error` type, wrapping all possible errors}} > > A crate can implement its own `Error` type, wrapping all possible errors. > It must be careful to make this type exception-safe (RFC 1236), and implement > `Error + Send + Sync + 'static` as well as `Display`. + +To ensure that the above recommendation is implemented correctly, you can use the following code: +```rust +pub enum Error { + ... // Implement Error enum here +} -> ### Recommendation {{#check LANG-ERRDO | Use the `?` operator and do not use the `try!` macro}} +#[cfg(test)] +mod test { + fn rfc1236(){} + + #[test] + fn test_rfc1236(){ + rfc1236::(); + } + +} +``` +> ### Recommendation {{#check LANG-ERR-FLAT | root positioning of type `Error` }} > -> The `?` operator should be used to improve readability of code. -> The `try!` macro should not be used. +> It is advisable to publicly position this type at the root of your API. For example: `crate::Error`. + +To do this, you can flatten the `Result` and `Error` types using the following piece of code placed at the root of the `src/lib.rs` or `src/main.rs` file: + +```rust +pub use error::Error; +``` + +The advantage of this technique is that, in your code, you can make the position of the type in your project agnostic for the user or for yourself. + +When using external libraries, you can either wrap the type or use a library such as [derive_more]. + +[derive_more]: https://crates.io/crates/derive_more +### Third-party library use Third-party crates may be used to facilitate error handling. Most of them (notably [failure], [snafu], [thiserror]) address the creation of new custom diff --git a/src/fr/04_language.md b/src/fr/04_language.md index 27b52a1..e346038 100644 --- a/src/fr/04_language.md +++ b/src/fr/04_language.md @@ -124,14 +124,17 @@ else { println!("{}", res); } ## Gestion des erreurs - - Le type `Result` est la façon privilégiée en Rust pour décrire le type de retour des fonctions dont le traitement peut échouer. Un objet `Result` doit être testé et jamais ignoré. +> ### Recommandation {{#check LANG-ERRDO | Utilisation de l'opérateur `?` et non-utilisation de la macro `try!`}} +> +> L'opérateur `?` doit être utilisé pour améliorer la lisibilité du code. +> La macro `try!` ne doit pas être utilisée. + +### Implémentation d'un type d'Erreur personnalisé + > ### Recommandation {{#check LANG-ERRWRAP | Mise en place d'un type `Error` personnalisé, pouvant contenir toutes les erreurs possibles}} > > Une *crate* peut implanter son propre type `Error` qui peut contenir toutes @@ -139,10 +142,39 @@ testé et jamais ignoré. > ce type doit être *exception-safe* (RFC 1236) et implémenter les traits > `Error + Send + Sync + 'static` ainsi que `Display`. -> ### Recommandation {{#check LANG-ERRDO | Utilisation de l'opérateur `?` et non-utilisation de la macro `try!`}} +Pour s'assurer que la recommandation ci-dessus soit bien implémenter, vous pouvez utiliser le code suivant : +```rust +pub enum Error { + ... // Implement Error enum here +} + +#[cfg(test)] +mod test { + fn rfc1236(){} + + #[test] + fn test_rfc1236(){ + rfc1236::(); + } + +} +``` + +> ### Recommandation {{#check LANG-ERR-FLAT | positionnement à la racine du type `Error` }} > -> L'opérateur `?` doit être utilisé pour améliorer la lisibilité du code. -> La macro `try!` ne doit pas être utilisée. +> Il est conseillé de positionner publiquement ce type à la racine de votre API. Par exemple : `crate::Error`. + +Pour ce faire, vous pouvez aplatir les types `Result` et `Error` via le morceau de code suivant placé à la racine du fichier `src/lib.rs` ou `src/main.rs` : +```rust +pub use error::Error; +``` + +L'avantage de cette technique, est qu'elle permet, dans votre code, de rendre agnostique pour l'utilisateur ou pour vous-même, la position du type dans votre projet. + +Lorsque vous utilisez des bibliothèques externes, vous pouvez soit wrapper le type soit utiliser une bibliothèque comme [derive_more]. + +[derive_more]: https://crates.io/crates/derive_more +### Utilisation de bibliothèque tierce Des *crates* tierces peuvent être utilisées pour faciliter la gestion d'erreurs. La plupart ([failure], [snafu], [thiserror]) proposent la création de types