Skip to content

Commit

Permalink
Improving error handling's best practices
Browse files Browse the repository at this point in the history
`+` Test to comply with RFC1236
`+` Error API recommendation

`+` French and english version
  • Loading branch information
Sans-Atout committed Aug 25, 2024
1 parent 47c3727 commit e8be56f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 14 deletions.
46 changes: 39 additions & 7 deletions src/en/04_language.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,25 +112,57 @@ else { println!("{}", res); }
> specialized functions `overflowing_<op>`, `wrapping_<op>`, or the
> `Wrapping` type must be used.


## Error handling

<!-- <mark>TODO</mark>: explicit good practices in 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<T: std::error::Error + Send + Sync + 'static >(){}

#[test]
fn test_rfc1236(){
rfc1236::<super::Error>();
}

}
```
> ### 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
Expand Down
46 changes: 39 additions & 7 deletions src/fr/04_language.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,25 +124,57 @@ else { println!("{}", res); }
## Gestion des erreurs

<!--
<mark>TODO</mark>: décrire les bonnes pratiques de gestion d'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
> les erreurs possibles. Des précautions supplémentaires doivent être prises :
> 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<T: std::error::Error + Send + Sync + 'static >(){}

#[test]
fn test_rfc1236(){
rfc1236::<super::Error>();
}

}
```

> ### 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
Expand Down

0 comments on commit e8be56f

Please sign in to comment.