Leçons du module (2/2)
Gestion des erreurs et opérateur ?
La philosophie Rust encourage à reconnaître explicitement la possibilité d'erreurs et à structurer le code pour les gérer avant la compilation du programme.
Les erreurs dans Rust sont divisées en deux catégories principales : les erreurs irrécupérables (provoquant un blocage immédiat via la panique panic!) et les erreurs récupérables (gérées via le type Result<T, E>).
Le type Résultat<T, E> et la correspondance de modèle
La plupart des erreurs récupérables renvoient un type Result<T, E> qui est une énumération définie comme :
enum Result<T, E> {
Ok(T),
Err(E),
}
Nous pouvons utiliser la correspondance de modèles pour inspecter le résultat :
use std::fs::File;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problema nell'aprire il file: {:?}", error),
};
}
Propagation d'erreur avec l'opérateur ?
Lorsque nous implémentons une fonction, au lieu de gérer l'erreur directement à l'intérieur de celle-ci, nous souhaitons souvent renvoyer l'erreur à l'appelant afin qu'il puisse décider quoi faire. Ce processus est appelé propagation des erreurs.
Rust fournit l'opérateur ? comme raccourci syntaxique pour la propagation des erreurs. Si la valeur d'un Result est Ok, la valeur contenue dans Ok est renvoyée par l'expression ; s'il s'agit de Err, l'erreur Err est renvoyée par toute la fonction actuelle comme si nous avions utilisé un return Err(...).
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("username.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}
[!IMPORTANT] L'opérateur
?ne peut être utilisé que dans les fonctions qui renvoient un type compatible avec la valeur à laquelle il est appliqué (généralementResult,Optionou des types qui implémententFromResidual).
Erreurs personnalisées
Nous pouvons définir nos propres types d'erreur en implémentant le trait std::fmt::Display (et éventuellement std::error::Error) pour fournir une représentation lisible par l'homme de l'erreur :
use std::fmt;
#[derive(Debug)]
struct MyError {
details: String,
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Errore: {}", self.details)
}
}
impl std::error::Error for MyError {}
Essayez-le vous-même
Exercice 1 : Lire un fichier avec le ? opérateur
Complétez la fonction read_username_from_file pour utiliser le ? opérateur. pour propager les erreurs générées en ouvrant le fichier et en lisant son contenu dans la chaîne du nom d'utilisateur.
Afficher l'indice
Ajoutez le caractère `?` après`File::open(...)`et après`username_file.read_to_string(...)` pour propager les erreurs.
Solution disponible après 3 tentatives
Exercice 2 : Analyser et traiter un nombre
Écrivez une fonction appelée parse_and_double qui prend en entrée une référence de chaîne &str et renvoie un Result<i32, std::num::ParseIntError>. La fonction doit analyser la chaîne avec val.parse::<i32>() en utilisant ? pour propager l'erreur d'analyse, et en cas de succès, renvoyer la valeur doublée entre Ok.
Afficher l'indice
Utilisez `val.parse::<i32>()?`pour convertir la chaîne en propageant l'erreur, puis retournez`Ok(num * 2)`.
Solution disponible après 3 tentatives
Exercice 3 : Définir une erreur personnalisée
Définissez une structure vide appelée CustomError. Implémentez le trait std::fmt::Display en écrivant le message « Une erreur personnalisée s'est produite » dans la méthode fmt.
Afficher l'indice
Déclarez `struct CustomError;`et implémentez Display en écrivant`write!(f, "Si e verificato un errore personalizzato")`dans la méthode`fmt`.
Solution disponible après 3 tentatives