#16 Gestion des erreurs
Result, ?, erreurs custom
Panic vs Result
Rust offre deux mécanismes pour gérer les erreurs : panic! pour les erreurs irrécupérables et Result pour les erreurs récupérables.
fn main() {
// panic! arrête immédiatement le programme
// À utiliser seulement pour des bugs/situations impossibles
panic!("Quelque chose de terrible est arrivé !");
// Exemples qui causent une panique :
// let v = vec![1, 2, 3];
// v[99]; // index hors limites -> panic!
}use std::fs;
fn main() {
// Result permet de gérer l'erreur proprement
let contenu = fs::read_to_string("config.toml");
match contenu {
Ok(texte) => println!("Config : {}", texte),
Err(e) => println!("Impossible de lire le fichier : {}", e),
}
// unwrap_or_else : fournir une valeur de repli
let texte = fs::read_to_string("config.toml")
.unwrap_or_else(|_| String::from("configuration par défaut"));
println!("{}", texte);
}Types d'erreur personnalisés
Pour les projets réels, on crée souvent ses propres types d'erreur qui regroupent toutes les erreurs possibles de l'application.
use std::fmt;
use std::io;
use std::num;
#[derive(Debug)]
enum AppError {
Io(io::Error),
Parse(num::ParseIntError),
Custom(String),
}
// Implémenter Display pour afficher l'erreur
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::Io(e) => write!(f, "erreur I/O : {}", e),
AppError::Parse(e) => write!(f, "erreur de parsing : {}", e),
AppError::Custom(msg) => write!(f, "{}", msg),
}
}
}
// Implémenter std::error::Error
impl std::error::Error for AppError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AppError::Io(e) => Some(e),
AppError::Parse(e) => Some(e),
AppError::Custom(_) => None,
}
}
}L'opérateur ? en profondeur
L'opérateur ? fait plus que simplement propager les erreurs : il appelle automatiquement From::from() pour convertir le type d'erreur si nécessaire.
use std::fs;
use std::io;
fn lire_nombre_depuis_fichier(chemin: &str) -> Result<i32, AppError> {
// ? convertit io::Error en AppError grâce à From
let contenu = fs::read_to_string(chemin)?;
// ? convertit ParseIntError en AppError grâce à From
let nombre: i32 = contenu.trim().parse()?;
Ok(nombre)
}
fn main() {
match lire_nombre_depuis_fichier("nombre.txt") {
Ok(n) => println!("Nombre lu : {}", n),
Err(e) => println!("Erreur : {}", e),
}
}Conversion d'erreurs avec From
Le trait From permet à l'opérateur ? de convertir automatiquement les types d'erreur. Il suffit d'implémenter From pour chaque type d'erreur source.
use std::io;
use std::num;
// Convertir io::Error en AppError
impl From<io::Error> for AppError {
fn from(e: io::Error) -> AppError {
AppError::Io(e)
}
}
// Convertir ParseIntError en AppError
impl From<num::ParseIntError> for AppError {
fn from(e: num::ParseIntError) -> AppError {
AppError::Parse(e)
}
}
// Maintenant ? fonctionne automatiquement !
fn charger_config(chemin: &str) -> Result<i32, AppError> {
let contenu = fs::read_to_string(chemin)?; // io::Error -> AppError
let port: i32 = contenu.trim().parse()?; // ParseIntError -> AppError
Ok(port)
}Le crate anyhow
Pour les applications (pas les bibliothèques), le crate anyhow simplifie énormément la gestion d'erreurs. Il fournit un type anyhow::Result qui accepte n'importe quelle erreur, et context() pour ajouter des messages explicatifs.
[dependencies]
anyhow = "1"use anyhow::{Context, Result};
use std::fs;
fn lire_config(chemin: &str) -> Result<String> {
let contenu = fs::read_to_string(chemin)
.context(format!("erreur de lecture du fichier {}", chemin))?;
Ok(contenu)
}
fn lire_port(chemin: &str) -> Result<u16> {
let contenu = lire_config(chemin)?;
let port: u16 = contenu.trim().parse()
.context("le fichier ne contient pas un numéro de port valide")?;
Ok(port)
}
fn main() {
match lire_config("config.toml") {
Ok(config) => println!("Config : {}", config),
Err(e) => {
println!("Erreur d'application : {}", e);
// Afficher la chaîne de causes
for cause in e.chain().skip(1) {
println!(" Cause : {}", cause);
}
}
}
}À vous de jouer
Essayez les commandes ci-dessous pour compiler et exécuter le programme :