Rust : Error handling

De Justine's wiki
Version datée du 3 novembre 2022 à 09:15 par Justine (discussion | contributions) (Page créée avec «  <nowiki> fn main() { //Panic ! (Erreurs irrécupérables) //On peut causer une panique en faisant une action qui est une erreur, //ou avec la macro panic!(). //abort ou unwind //Lors d'une panique, par défaut, Rust va remonter la stack pour //libérer la mémoire (unwind). //On peut modifier ça en choisissant d'abort (ne rien faire) à la place //; c'est utile pour diminuer la taille du binaire. //Pour ça, ajouter la... »)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigation Aller à la recherche
fn main() {
    //Panic ! (Erreurs irrécupérables)
    //On peut causer une panique en faisant une action qui est une erreur,
    //ou avec la macro panic!().
    
    //abort ou unwind
    //Lors d'une panique, par défaut, Rust va remonter la stack pour 
    //libérer la mémoire (unwind).
    //On peut modifier ça en choisissant d'abort (ne rien faire) à la place
    //; c'est utile pour diminuer la taille du binaire.
    //Pour ça, ajouter la section suivante dans Cargo.toml
    //[profile.release]
    //panic = 'abort'

    //Panique
    panic!("Crash and burn");

    //On peut agir sur la backtrace d'erreur via la variable d'environnement
    //RUST_BACKTRACE (en la mettant à 1).
    

    //Result (Erreurs récupérables)
    //
    //Pour rappel, l'enum Result a la forme suivante:
    //enum Result<T, E> {
    //  Ok(T),
    //  Err(E)
    //}
    //T et E sont des types génériques. T est la valeur qui sera retournée
    //sur un succès, E le type d'erreur.
}

use std::fs::File;

fn open_file() {
    let greeting_file_result = File::open("hello.txt");
    //Le retour de File::open est un Result<T, E>. T sera ici un handler de  
    //fichier std::fs:File, E sera un std::io::Error. 

    //On rajoute du code de gestion d'erreur:
    let greeting_file  = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("Problem opening the file : {:?}", error),
    };
}

    //On peut vouloir régir différement en fonction des types d'erreurs.
use std::io::ErrorKind;

fn open_file_2() {
    let greeting_file_result = File::open("hello.txt");

    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Coulnd not create the file : {:?}", e)
            },
            //Ici, on est sur le bras catch-all : other_error est juste
            //un nom de variable au pif...
            other_error => {
                panic!("Problem opening the file {:?}", other_error)
            }
        }
    };
}
    
    //match c'est cool, mais en fait pas tellement.
    //On peut utiliser unwrap(), qui signifie : 
    //Si Ok, renvoie la valeur associée
    //Si Err, panic! 

fn blablabla() {
    let greeting_file = File::open("hello.txt").unwrap();
}
    //On a aussi expect. Expect fait un peu la même chose mais permet
    //de choisir le message de panic (Super, ça valait vraiment le coup
    //de faire 2 méthodes ?). C'est très souvent le meilleur choix. 
fn monculsurlacommode() {
    let greeting_file = File::open("hello.txt")
        .expect("hello.txt should be included in this project");
}


    //Propagation d'erreurs.
    //Quand une fonction rencontre une erreur, elle peut renvoyer son erreur
    //au lieu de la traiter elle-même; c'est la propagation.
    //On peut se contenter de renvoyer un result depuis la fonction si on veut:
    //Ainsi on renvoie soit un result(Ok) avec une valeur, soit Result<Err>
    //avec une erreur.
    //
    //Mais on a un shortcut : le ?
    //
    //Exemple SANS le ?
fn read_username_from_file() -> Result<String, io::Error> {
    let username_file_result = File::open("hello.txt");

    let mut username_file = match username_file_result {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut username = String::new();

    match username_file.read_to_string(&mut username) {
        Ok(_) => Ok(username),
        Err(e) => Err(e),
    }
}

//Exemple avec le ?
//Le résultat est le même qu'avec les match de la fonction au-dessus.
//Quand l'opérateur ? est utilisé, le type d'erreur reçu est converti
//en fonction du type d'erreur donné dans la signature de la fonction.
fn tropsuperlagestionderreur() -> Result<String, io::Error> {
    //Ici, le ? signifie:
    //Si OK : Renvoyer la valeur V dans Ok(V) à username_file;
    //Si Err : Renvoyer l'erreur au code appellant la fonction.
    //? ne peut être utilisé que si la fonction renvoie un result 
    //ou un Option<T>
    let mut username_file = File::open("hello.txt")?;
    let mut username = String::new();
    username_file.read_to_string(&mut username)?;
    Ok(username)
    //On peut même enchaîner derrière un ?
    //File::open("hello.txt")?.read_to_string(&mut username)?;
    //Ok(username)
}

//Et encore plus court
fn read_username_from_file2() -> Result<String, io::Error> {
    fs::read_to_string("hello.txt")
}

//Fonctionne aussi avec un Option<T> (Some, None, tout ça)
fn last_char_of_first_line(text: &str) -> Option<char> {
    //Ici next() renvoie un Option<str>
    //Va renvoyer None si il n'ya rien (sans prendre en compte
    //chars().last() et donc pas d'erreur), 
    //Va extraire le str et le filer à la méthode d'après (chars())
    //sinon
    text.lines().next()?.chars().last()
}

//On peut donc utiliser ? sur un Result ou une Option, mais
//il ne va pas convertir l'un en l'autre : on doit respecter la 
//signature de la fonction. On peut alors utiliser ok() sur un result ou
//ok_or() sur une option.
//
//BTW, une fonction main peut renvoyer des erreurs, que l'on peut d'ailleurs gérer
//avec ?; si c'est le cas, l'exécutable renverra 0 si Ok et autre chose si erreur.